/*
 * iuiu.js
 * http://github.com/KumaWang/iuiu.js/
 *
 * Copyright 2018 KumaWang
 * Released under the MIT license
 */
var L2D = (function() {

// src/1-1.require.js
//使用沙箱模式防止污染外面的变量
(function () {
    //让外面可以只能访问到require变量
    window.require = require;
    //定义一个加载模块的方法
    function require(moduleName, callback) {
        //创建加载模块的具体实现类
        var requireHelper = new RequireHelper(moduleName, callback);
        //调用加载模块类的load方法加载模块
        requireHelper.load();
    }
    //存储已加载模块的信息
    var moduleList = [];

    //创建一个实体类,给传进来的属性赋值
    function RequireHelper(moduleName, callback) {
        this.moduleName = moduleName;
        this.callback = callback;
    }

    //给模块加载实现类的原型添加方法
    RequireHelper.prototype = {
        //加载模块
        load: function () {
            var that = this;
            var moduleName = that.moduleName;
            if (that.isLoad()) {//模块已被加载(资源优化:已经请求的模块不要再次加载)
                var moduleInfo = that.getModuleInfo();//获取模块的描述信息
                if (moduleList.isLoad) {//如果模块资源已加载完成
                    if(that.callback) that.callback();//可以放心的调用模块对应的回调函数
                } else {//模块资源没加载完
                    var oldCallback = moduleInfo.callback;//取出之前的回调函数
                    moduleInfo.callback = function () {//追加回调函数
                        oldCallback();
                        if(that.callback) that.callback();
                    };
                }
            } else {//模块没有加载
                var script = document.createElement("script");
                script.src = that.moduleName;
                document.getElementsByTagName("head")[0].appendChild(script);//加载模块
                var moduleInfo = {
                    moduleName: that.moduleName, isLoad: false, callback: function () {
                        if(that.callback) that.callback();
                    }
                };//添加模块的描述信息
                script.onload = function () {
                    moduleInfo.callback();//执行模块对应的回调函数
                    moduleInfo.isLoad = true;//标识模块资源被加载完成
                }
            }
        },
        //判断指定模块是否加载
        isLoad: function () {
            return this.getModuleInfo() == null ? false : true;            
        },
        //根据模块名称获取模块信息
        getModuleInfo: function () {
            for (var i = 0; i < moduleList.length; i++) {
                if (that.moduleName == moduleList[i].name) {
                    return moduleList;
                }
            }
            return null;
        }
    };

})(window)
// src/1-2.loader.js
var parseINIString = function (data){ 
    var regex = { 
        section: /^\s*\[\s*([^\]]*)\s*\]\s*$/, 
        param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/, 
        comment: /^\s*;.*$/ 
    }; 
    var value = {}; 
    var lines = data.split(/\r\n|\r|\n/); 
    var section = null; 
    lines.forEach(function(line){ 
    if(regex.comment.test(line)){ 
        return; 
    }else if(regex.param.test(line)){ 
        var match = line.match(regex.param); 
        if(section){ 
            value[section][match[1]] = match[2]; 
        }else{ 
            value[match[1]] = match[2]; 
        } 
    }else if(regex.section.test(line)){ 
        var match = line.match(regex.section); 
        value[match[1]] = {}; 
        section = match[1]; 
    }else if(line.length == 0 && section){ 
        section = null; 
    }; 
    });
    
    return value; 
}

function IniLoader(loader) {
    this.loader = loader;
};
IniLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params) {
        return parseINIString(buffer);
    }
};

function PackageLoader(loader) {
    this.loader = loader;
}
PackageLoader.prototype = {
    responseType : 'arraybuffer',
    load : function(buffer, params) {
        if(buffer) {
            var content = {};
            var dataView = new DataView(buffer);
            var originalBuffer = readHeader(content, dataView);
            
            // 解压缩文件
            if(content.flags == 1) {
                var compressed = new Uint8Array(buffer, originalBuffer.position, originalBuffer.length);
                var decompressed = lz4.decompress(compressed)
                var arrayBuffer = new ArrayBuffer(decompressed.length);
                for(var i = 0; i < decompressed.length; i++) {
                    arrayBuffer[i] = decompressed[i];
                }
                
                originalBuffer = new BinaryReader(new DataView(arrayBuffer), 0, arrayBuffer.length);
            }

            // 读取实际内容
            readContent(content, originalBuffer);
        }
        else {
            // 失效的资源
            //content.valid = false;
            //content.errorMessage = '无效的资产源:' + content.src;
        }
    },
    readHeader : function(content, buffer) {
        var br = new BinaryReader(buffer);
        // 头校验
        var r = br.readChar();
        var e = br.readChar();
        var s = br.readChar();

        if(r != 'm' || e != 'r' || s != 'f') {
            throw '存在无效包文件';
        }

        // 读取平台
        var platform = br.readByte();
        
        // 读取文件格式
        var format = br.readByte();
        
        // 读取flags
        var flags = br.readByte();
        
        // 读取内容大小
        var contentSize = br.readInt32();

        // 预存数据
        var holdSize = br.readInt32();

        content.platform = platform;
        content.format = format;
        content.flags = flags;
        content.contentSize = contentSize;

        // 返回压缩数据大小
        return new BinaryReader(buffer, br.position, content.contentSize);
    },
    readContent : function(content, buffer) {
        var header = {};
        header.name = buffer.readString();
        header.version = {};
        header.version.major = buffer.readInt32();
        header.version.minor = buffer.readInt32();
        header.version.build = buffer.readInt32();
        header.version.revision = buffer.readInt32();
        
        var iconData = buffer.readString();
        var description = buffer.readString();
        var references = [];
        var files = [];
        
        var count = buffer.readInt32();
        for(var i = 0; i < count; i++) {
            var header2 = {};
            header2.name = buffer.readString();
            header2.version = {};
            header2.version.major = buffer.readInt32();
            header2.version.minor = buffer.readInt32();
            header2.version.build = buffer.readInt32();
            header2.version.revision = buffer.readInt32();
            
            references.push({ header : header2 });
        }
        
        count = buffer.readInt32();
        for(var i = 0; i < count;i ++) {
            var inculde = buffer.readString();
            var data = buffer.readString();
            files.push({ inculde : inculde, data : data });
        }
        
        content.header = header;
        //content.description = description;
        //content.
        content.files = files;
        content.references = reference;
    }
};

function JsonLoader(loader) {
    this.loader = loader;
}
JsonLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, entry) {
        return JSON.parse(buffer);
    },
	create: function() {
		return null;
	}
}

function Loader(domain) {
    this.domain = domain;
    //this.checklist = window.localStorage.domain
    
    // modes
    this.loaders = {};
    this.addMode('ini', new IniLoader(this));
    this.addMode('json', new JsonLoader(this));
}

Loader.prototype = {
    // ### .addMode(name, loader)
    // @param loader
    //          method load
    addMode : function(name, loader) {
        this.loaders[name] = loader;
    },
    
    // ### .load(fileName[, type])
    // @param type
    //          content
    //          ini 
    //          image
    load : function(fileName, userToken, callback, params) {
        var scope = this;
        var fileNameExt = fileName.lastIndexOf(".");//取到文件名开始到最后一个点的长度
        var fileNameLength = fileName.length;//取到文件名长度
        var fileFormat = fileName.substring(fileNameExt + 1, fileNameLength);//截
        
        var type = fileFormat;
        var loader = this.loaders[type];
        
        var content =  { status : 'error', params : params, callbacks : [], content : loader.create(fileName) };          
        if(content.status == 'error') {            
            if(loader) {
                if(callback) content.callbacks.push(callback);
                
                content.src = fileName;
                content.status = 'loading';
                content.userToken = userToken;
                content.type = type;
                if(loader.responseType) {
                    var request = new XMLHttpRequest();
                    request.responseType = loader.responseType;
                    request.open("GET", (this.domain != null ? this.domain + '/' : '') + fileName); // + (cache ? '' : '?' + new Date().toString()), true);
                    request.content = content;
                    request.loader = loader;
                    if(!request.loader) {
                        throw 'no dencoder';
                    }
                    
                    request.onload = function(e) {
                        var loader = e.currentTarget.loader;
                        var content = e.currentTarget.content;
                        try {
                            //content.md5 = CryptoJS.MD5(e.currentTarget.response);
                            content.content = loader.load(e.currentTarget.response, content.params, content.content);
                            content.status = 'loaded';
                        }
                        catch(error) {
							console.log(error);
                            content.status = 'error';
                            if(content.onerror) 
                                content.onerror(content);
                        }
                            
                        if(content.status == 'loaded') {
                            for(var i = 0; i < content.callbacks.length; i++) {
                                content.callbacks[i](content);
                            }
                            content.callbacks = [];
                        }
                    };
                    request.onerror = function(e) {
                        var content = e.currentTarget.content;
                        content.status = 'error';
                        if(content.onerror) 
                            content.onerror(content);
                        //content.errorMessage = 'Error ' + e.target.status + ' occurred while receiving the document.'
                    };
                    request.send();
                    
                    // 如果是同步资源
                    if(request.loader.sync) {
                        // 暂停循环
                        
                    }
                } else {
                    throw 'responseType';
                    //content.content = loader.load(fileName);
                }
            } else {
                throw 'unkonwn response type';
            }
        }
        else if(content.status == "loading") {
            if(callback) content.callbacks.push(callback);
        }
        else if(content.status == 'loaded') {
            if(callback) callback(content);
        }
        
        return content.content;
    }
};
// src/1-3.keyboard.js
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = global || self, global.keyboardJS = factory());
  }(this, (function () { 'use strict';
  
    function _typeof(obj) {
      "@babel/helpers - typeof";
  
      if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function (obj) {
          return typeof obj;
        };
      } else {
        _typeof = function (obj) {
          return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
      }
  
      return _typeof(obj);
    }
  
    function _classCallCheck(instance, Constructor) {
      if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
      }
    }
  
    function _defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }
  
    function _createClass(Constructor, protoProps, staticProps) {
      if (protoProps) _defineProperties(Constructor.prototype, protoProps);
      if (staticProps) _defineProperties(Constructor, staticProps);
      return Constructor;
    }
  
    function _toConsumableArray(arr) {
      return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
    }
  
    function _arrayWithoutHoles(arr) {
      if (Array.isArray(arr)) return _arrayLikeToArray(arr);
    }
  
    function _iterableToArray(iter) {
      if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
    }
  
    function _unsupportedIterableToArray(o, minLen) {
      if (!o) return;
      if (typeof o === "string") return _arrayLikeToArray(o, minLen);
      var n = Object.prototype.toString.call(o).slice(8, -1);
      if (n === "Object" && o.constructor) n = o.constructor.name;
      if (n === "Map" || n === "Set") return Array.from(o);
      if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
    }
  
    function _arrayLikeToArray(arr, len) {
      if (len == null || len > arr.length) len = arr.length;
  
      for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
  
      return arr2;
    }
  
    function _nonIterableSpread() {
      throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
    }
  
    var KeyCombo = /*#__PURE__*/function () {
      function KeyCombo(keyComboStr) {
        _classCallCheck(this, KeyCombo);
  
        this.sourceStr = keyComboStr;
        this.subCombos = KeyCombo.parseComboStr(keyComboStr);
        this.keyNames = this.subCombos.reduce(function (memo, nextSubCombo) {
          return memo.concat(nextSubCombo);
        }, []);
      }
  
      _createClass(KeyCombo, [{
        key: "check",
        value: function check(pressedKeyNames) {
          var startingKeyNameIndex = 0;
  
          for (var i = 0; i < this.subCombos.length; i += 1) {
            startingKeyNameIndex = this._checkSubCombo(this.subCombos[i], startingKeyNameIndex, pressedKeyNames);
  
            if (startingKeyNameIndex === -1) {
              return false;
            }
          }
  
          return true;
        }
      }, {
        key: "isEqual",
        value: function isEqual(otherKeyCombo) {
          if (!otherKeyCombo || typeof otherKeyCombo !== 'string' && _typeof(otherKeyCombo) !== 'object') {
            return false;
          }
  
          if (typeof otherKeyCombo === 'string') {
            otherKeyCombo = new KeyCombo(otherKeyCombo);
          }
  
          if (this.subCombos.length !== otherKeyCombo.subCombos.length) {
            return false;
          }
  
          for (var i = 0; i < this.subCombos.length; i += 1) {
            if (this.subCombos[i].length !== otherKeyCombo.subCombos[i].length) {
              return false;
            }
          }
  
          for (var _i = 0; _i < this.subCombos.length; _i += 1) {
            var subCombo = this.subCombos[_i];
  
            var otherSubCombo = otherKeyCombo.subCombos[_i].slice(0);
  
            for (var j = 0; j < subCombo.length; j += 1) {
              var keyName = subCombo[j];
              var index = otherSubCombo.indexOf(keyName);
  
              if (index > -1) {
                otherSubCombo.splice(index, 1);
              }
            }
  
            if (otherSubCombo.length !== 0) {
              return false;
            }
          }
  
          return true;
        }
      }, {
        key: "_checkSubCombo",
        value: function _checkSubCombo(subCombo, startingKeyNameIndex, pressedKeyNames) {
          subCombo = subCombo.slice(0);
          pressedKeyNames = pressedKeyNames.slice(startingKeyNameIndex);
          var endIndex = startingKeyNameIndex;
  
          for (var i = 0; i < subCombo.length; i += 1) {
            var keyName = subCombo[i];
  
            if (keyName[0] === '\\') {
              var escapedKeyName = keyName.slice(1);
  
              if (escapedKeyName === KeyCombo.comboDeliminator || escapedKeyName === KeyCombo.keyDeliminator) {
                keyName = escapedKeyName;
              }
            }
  
            var index = pressedKeyNames.indexOf(keyName);
  
            if (index > -1) {
              subCombo.splice(i, 1);
              i -= 1;
  
              if (index > endIndex) {
                endIndex = index;
              }
  
              if (subCombo.length === 0) {
                return endIndex;
              }
            }
          }
  
          return -1;
        }
      }]);
  
      return KeyCombo;
    }();
    KeyCombo.comboDeliminator = '>';
    KeyCombo.keyDeliminator = '+';
  
    KeyCombo.parseComboStr = function (keyComboStr) {
      var subComboStrs = KeyCombo._splitStr(keyComboStr, KeyCombo.comboDeliminator);
  
      var combo = [];
  
      for (var i = 0; i < subComboStrs.length; i += 1) {
        combo.push(KeyCombo._splitStr(subComboStrs[i], KeyCombo.keyDeliminator));
      }
  
      return combo;
    };
  
    KeyCombo._splitStr = function (str, deliminator) {
      var s = str;
      var d = deliminator;
      var c = '';
      var ca = [];
  
      for (var ci = 0; ci < s.length; ci += 1) {
        if (ci > 0 && s[ci] === d && s[ci - 1] !== '\\') {
          ca.push(c.trim());
          c = '';
          ci += 1;
        }
  
        c += s[ci];
      }
  
      if (c) {
        ca.push(c.trim());
      }
  
      return ca;
    };
  
    var Locale = /*#__PURE__*/function () {
      function Locale(name) {
        _classCallCheck(this, Locale);
  
        this.localeName = name;
        this.activeTargetKeys = [];
        this.pressedKeys = [];
        this._appliedMacros = [];
        this._keyMap = {};
        this._killKeyCodes = [];
        this._macros = [];
      }
  
      _createClass(Locale, [{
        key: "bindKeyCode",
        value: function bindKeyCode(keyCode, keyNames) {
          if (typeof keyNames === 'string') {
            keyNames = [keyNames];
          }
  
          this._keyMap[keyCode] = keyNames;
        }
      }, {
        key: "bindMacro",
        value: function bindMacro(keyComboStr, keyNames) {
          if (typeof keyNames === 'string') {
            keyNames = [keyNames];
          }
  
          var handler = null;
  
          if (typeof keyNames === 'function') {
            handler = keyNames;
            keyNames = null;
          }
  
          var macro = {
            keyCombo: new KeyCombo(keyComboStr),
            keyNames: keyNames,
            handler: handler
          };
  
          this._macros.push(macro);
        }
      }, {
        key: "getKeyCodes",
        value: function getKeyCodes(keyName) {
          var keyCodes = [];
  
          for (var keyCode in this._keyMap) {
            var index = this._keyMap[keyCode].indexOf(keyName);
  
            if (index > -1) {
              keyCodes.push(keyCode | 0);
            }
          }
  
          return keyCodes;
        }
      }, {
        key: "getKeyNames",
        value: function getKeyNames(keyCode) {
          return this._keyMap[keyCode] || [];
        }
      }, {
        key: "setKillKey",
        value: function setKillKey(keyCode) {
          if (typeof keyCode === 'string') {
            var keyCodes = this.getKeyCodes(keyCode);
  
            for (var i = 0; i < keyCodes.length; i += 1) {
              this.setKillKey(keyCodes[i]);
            }
  
            return;
          }
  
          this._killKeyCodes.push(keyCode);
        }
      }, {
        key: "pressKey",
        value: function pressKey(keyCode) {
          if (typeof keyCode === 'string') {
            var keyCodes = this.getKeyCodes(keyCode);
  
            for (var i = 0; i < keyCodes.length; i += 1) {
              this.pressKey(keyCodes[i]);
            }
  
            return;
          }
  
          this.activeTargetKeys.length = 0;
          var keyNames = this.getKeyNames(keyCode);
  
          for (var _i = 0; _i < keyNames.length; _i += 1) {
            this.activeTargetKeys.push(keyNames[_i]);
  
            if (this.pressedKeys.indexOf(keyNames[_i]) === -1) {
              this.pressedKeys.push(keyNames[_i]);
            }
          }
  
          this._applyMacros();
        }
      }, {
        key: "releaseKey",
        value: function releaseKey(keyCode) {
          if (typeof keyCode === 'string') {
            var keyCodes = this.getKeyCodes(keyCode);
  
            for (var i = 0; i < keyCodes.length; i += 1) {
              this.releaseKey(keyCodes[i]);
            }
          } else {
            var keyNames = this.getKeyNames(keyCode);
  
            var killKeyCodeIndex = this._killKeyCodes.indexOf(keyCode);
  
            if (killKeyCodeIndex !== -1) {
              this.pressedKeys.length = 0;
            } else {
              for (var _i2 = 0; _i2 < keyNames.length; _i2 += 1) {
                var index = this.pressedKeys.indexOf(keyNames[_i2]);
  
                if (index > -1) {
                  this.pressedKeys.splice(index, 1);
                }
              }
            }
  
            this.activeTargetKeys.length = 0;
  
            this._clearMacros();
          }
        }
      }, {
        key: "_applyMacros",
        value: function _applyMacros() {
          var macros = this._macros.slice(0);
  
          for (var i = 0; i < macros.length; i += 1) {
            var macro = macros[i];
  
            if (macro.keyCombo.check(this.pressedKeys)) {
              if (macro.handler) {
                macro.keyNames = macro.handler(this.pressedKeys);
              }
  
              for (var j = 0; j < macro.keyNames.length; j += 1) {
                if (this.pressedKeys.indexOf(macro.keyNames[j]) === -1) {
                  this.pressedKeys.push(macro.keyNames[j]);
                }
              }
  
              this._appliedMacros.push(macro);
            }
          }
        }
      }, {
        key: "_clearMacros",
        value: function _clearMacros() {
          for (var i = 0; i < this._appliedMacros.length; i += 1) {
            var macro = this._appliedMacros[i];
  
            if (!macro.keyCombo.check(this.pressedKeys)) {
              for (var j = 0; j < macro.keyNames.length; j += 1) {
                var index = this.pressedKeys.indexOf(macro.keyNames[j]);
  
                if (index > -1) {
                  this.pressedKeys.splice(index, 1);
                }
              }
  
              if (macro.handler) {
                macro.keyNames = null;
              }
  
              this._appliedMacros.splice(i, 1);
  
              i -= 1;
            }
          }
        }
      }]);
  
      return Locale;
    }();
  
    var Keyboard = /*#__PURE__*/function () {
      function Keyboard(targetWindow, targetElement, targetPlatform, targetUserAgent) {
        _classCallCheck(this, Keyboard);
  
        this._locale = null;
        this._currentContext = '';
        this._contexts = {};
        this._listeners = [];
        this._appliedListeners = [];
        this._locales = {};
        this._targetElement = null;
        this._targetWindow = null;
        this._targetPlatform = '';
        this._targetUserAgent = '';
        this._isModernBrowser = false;
        this._targetKeyDownBinding = null;
        this._targetKeyUpBinding = null;
        this._targetResetBinding = null;
        this._paused = false;
        this._contexts.global = {
          listeners: this._listeners,
          targetWindow: targetWindow,
          targetElement: targetElement,
          targetPlatform: targetPlatform,
          targetUserAgent: targetUserAgent
        };
        this.setContext('global');
      }
  
      _createClass(Keyboard, [{
        key: "update",
        value: function update() {
            this._applyBindings();
        }  
      },{
        key: "setLocale",
        value: function setLocale(localeName, localeBuilder) {
          var locale = null;
  
          if (typeof localeName === 'string') {
            if (localeBuilder) {
              locale = new Locale(localeName);
              localeBuilder(locale, this._targetPlatform, this._targetUserAgent);
            } else {
              locale = this._locales[localeName] || null;
            }
          } else {
            locale = localeName;
            localeName = locale._localeName;
          }
  
          this._locale = locale;
          this._locales[localeName] = locale;
  
          if (locale) {
            this._locale.pressedKeys = locale.pressedKeys;
          }
  
          return this;
        }
      }, {
        key: "getLocale",
        value: function getLocale(localName) {
          localName || (localName = this._locale.localeName);
          return this._locales[localName] || null;
        }
      }, {
        key: "bind",
        value: function bind(keyComboStr, pressHandler, releaseHandler, preventRepeatByDefault) {
          if (keyComboStr === null || typeof keyComboStr === 'function') {
            preventRepeatByDefault = releaseHandler;
            releaseHandler = pressHandler;
            pressHandler = keyComboStr;
            keyComboStr = null;
          }
  
          if (keyComboStr && _typeof(keyComboStr) === 'object' && typeof keyComboStr.length === 'number') {
            for (var i = 0; i < keyComboStr.length; i += 1) {
              this.bind(keyComboStr[i], pressHandler, releaseHandler);
            }
  
            return this;
          }
  
          this._listeners.push({
            keyCombo: keyComboStr ? new KeyCombo(keyComboStr) : null,
            pressHandler: pressHandler || null,
            releaseHandler: releaseHandler || null,
            preventRepeat: false,
            preventRepeatByDefault: preventRepeatByDefault || false,
            executingHandler: false
          });
  
          return this;
        }
      }, {
        key: "addListener",
        value: function addListener(keyComboStr, pressHandler, releaseHandler, preventRepeatByDefault) {
          return this.bind(keyComboStr, pressHandler, releaseHandler, preventRepeatByDefault);
        }
      }, {
        key: "on",
        value: function on(keyComboStr, pressHandler, releaseHandler, preventRepeatByDefault) {
          return this.bind(keyComboStr, pressHandler, releaseHandler, preventRepeatByDefault);
        }
      }, {
        key: "bindPress",
        value: function bindPress(keyComboStr, pressHandler, preventRepeatByDefault) {
          return this.bind(keyComboStr, pressHandler, null, preventRepeatByDefault);
        }
      }, {
        key: "bindRelease",
        value: function bindRelease(keyComboStr, releaseHandler) {
          return this.bind(keyComboStr, null, releaseHandler, preventRepeatByDefault);
        }
      }, {
        key: "unbind",
        value: function unbind(keyComboStr, pressHandler, releaseHandler) {
          if (keyComboStr === null || typeof keyComboStr === 'function') {
            releaseHandler = pressHandler;
            pressHandler = keyComboStr;
            keyComboStr = null;
          }
  
          if (keyComboStr && _typeof(keyComboStr) === 'object' && typeof keyComboStr.length === 'number') {
            for (var i = 0; i < keyComboStr.length; i += 1) {
              this.unbind(keyComboStr[i], pressHandler, releaseHandler);
            }
  
            return this;
          }
  
          for (var _i = 0; _i < this._listeners.length; _i += 1) {
            var listener = this._listeners[_i];
            var comboMatches = !keyComboStr && !listener.keyCombo || listener.keyCombo && listener.keyCombo.isEqual(keyComboStr);
            var pressHandlerMatches = !pressHandler && !releaseHandler || !pressHandler && !listener.pressHandler || pressHandler === listener.pressHandler;
            var releaseHandlerMatches = !pressHandler && !releaseHandler || !releaseHandler && !listener.releaseHandler || releaseHandler === listener.releaseHandler;
  
            if (comboMatches && pressHandlerMatches && releaseHandlerMatches) {
              this._listeners.splice(_i, 1);
  
              _i -= 1;
            }
          }
  
          return this;
        }
      }, {
        key: "removeListener",
        value: function removeListener(keyComboStr, pressHandler, releaseHandler) {
          return this.unbind(keyComboStr, pressHandler, releaseHandler);
        }
      }, {
        key: "off",
        value: function off(keyComboStr, pressHandler, releaseHandler) {
          return this.unbind(keyComboStr, pressHandler, releaseHandler);
        }
      }, {
        key: "setContext",
        value: function setContext(contextName) {
          if (this._locale) {
            this.releaseAllKeys();
          }
  
          if (!this._contexts[contextName]) {
            var globalContext = this._contexts.global;
            this._contexts[contextName] = {
              listeners: [],
              targetWindow: globalContext.targetWindow,
              targetElement: globalContext.targetElement,
              targetPlatform: globalContext.targetPlatform,
              targetUserAgent: globalContext.targetUserAgent
            };
          }
  
          var context = this._contexts[contextName];
          this._currentContext = contextName;
          this._listeners = context.listeners;
          this.stop();
          this.watch(context.targetWindow, context.targetElement, context.targetPlatform, context.targetUserAgent);
          return this;
        }
      }, {
        key: "getContext",
        value: function getContext() {
          return this._currentContext;
        }
      }, {
        key: "withContext",
        value: function withContext(contextName, callback) {
          var previousContextName = this.getContext();
          this.setContext(contextName);
          callback();
          this.setContext(previousContextName);
          return this;
        }
      }, {
        key: "watch",
        value: function watch(targetWindow, targetElement, targetPlatform, targetUserAgent) {
          var _this = this;
  
          this.stop();
          var win = typeof globalThis !== 'undefined' ? globalThis : typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : {};
  
          if (!targetWindow) {
            if (!win.addEventListener && !win.attachEvent) {
              // This was added so when using things like JSDOM watch can be used to configure watch
              // for the global namespace manually.
              if (this._currentContext === 'global') {
                return;
              }
  
              throw new Error('Cannot find window functions addEventListener or attachEvent.');
            }
  
            targetWindow = win;
          } // Handle element bindings where a target window is not passed
  
  
          if (typeof targetWindow.nodeType === 'number') {
            targetUserAgent = targetPlatform;
            targetPlatform = targetElement;
            targetElement = targetWindow;
            targetWindow = win;
          }
  
          if (!targetWindow.addEventListener && !targetWindow.attachEvent) {
            throw new Error('Cannot find addEventListener or attachEvent methods on targetWindow.');
          }
  
          this._isModernBrowser = !!targetWindow.addEventListener;
          var userAgent = targetWindow.navigator && targetWindow.navigator.userAgent || '';
          var platform = targetWindow.navigator && targetWindow.navigator.platform || '';
          targetElement && targetElement !== null || (targetElement = targetWindow.document);
          targetPlatform && targetPlatform !== null || (targetPlatform = platform);
          targetUserAgent && targetUserAgent !== null || (targetUserAgent = userAgent);
  
          this._targetKeyDownBinding = function (event) {
            _this.pressKey(event.keyCode, event);
  
            _this._handleCommandBug(event, platform);
          };
  
          this._targetKeyUpBinding = function (event) {
            _this.releaseKey(event.keyCode, event);
          };
  
          this._targetResetBinding = function (event) {
            _this.releaseAllKeys(event);
          };
  
          this._bindEvent(targetElement, 'keydown', this._targetKeyDownBinding);
  
          this._bindEvent(targetElement, 'keyup', this._targetKeyUpBinding);
  
          this._bindEvent(targetWindow, 'focus', this._targetResetBinding);
  
          this._bindEvent(targetWindow, 'blur', this._targetResetBinding);
  
          this._targetElement = targetElement;
          this._targetWindow = targetWindow;
          this._targetPlatform = targetPlatform;
          this._targetUserAgent = targetUserAgent;
          var currentContext = this._contexts[this._currentContext];
          currentContext.targetWindow = this._targetWindow;
          currentContext.targetElement = this._targetElement;
          currentContext.targetPlatform = this._targetPlatform;
          currentContext.targetUserAgent = this._targetUserAgent;
          return this;
        }
      }, {
        key: "stop",
        value: function stop() {
          if (!this._targetElement || !this._targetWindow) {
            return;
          }
  
          this._unbindEvent(this._targetElement, 'keydown', this._targetKeyDownBinding);
  
          this._unbindEvent(this._targetElement, 'keyup', this._targetKeyUpBinding);
  
          this._unbindEvent(this._targetWindow, 'focus', this._targetResetBinding);
  
          this._unbindEvent(this._targetWindow, 'blur', this._targetResetBinding);
  
          this._targetWindow = null;
          this._targetElement = null;
          return this;
        }
      }, {
        key: "pressKey",
        value: function pressKey(keyCode, event) {
          if (this._paused) {
            return this;
          }
  
          if (!this._locale) {
            throw new Error('Locale not set');
          }
  
          this._locale.pressKey(keyCode);
          return this;
        }
      }, {
        key: "releaseKey",
        value: function releaseKey(keyCode, event) {
          if (this._paused) {
            return this;
          }
  
          if (!this._locale) {
            throw new Error('Locale not set');
          }
  
          this._locale.releaseKey(keyCode);
  
          this._clearBindings(event);
  
          return this;
        }
      }, {
        key: "releaseAllKeys",
        value: function releaseAllKeys(event) {
          if (this._paused) {
            return this;
          }
  
          if (!this._locale) {
            throw new Error('Locale not set');
          }
  
          this._locale.pressedKeys.length = 0;
  
          this._clearBindings(event);
  
          return this;
        }
      }, {
        key: "pause",
        value: function pause() {
          if (this._paused) {
            return this;
          }
  
          if (this._locale) {
            this.releaseAllKeys();
          }
  
          this._paused = true;
          return this;
        }
      }, {
        key: "resume",
        value: function resume() {
          this._paused = false;
          return this;
        }
      }, {
        key: "reset",
        value: function reset() {
          this.releaseAllKeys();
          this._listeners.length = 0;
          return this;
        }
      }, {
        key: "_bindEvent",
        value: function _bindEvent(targetElement, eventName, handler) {
          return this._isModernBrowser ? targetElement.addEventListener(eventName, handler, false) : targetElement.attachEvent('on' + eventName, handler);
        }
      }, {
        key: "_unbindEvent",
        value: function _unbindEvent(targetElement, eventName, handler) {
          return this._isModernBrowser ? targetElement.removeEventListener(eventName, handler, false) : targetElement.detachEvent('on' + eventName, handler);
        }
      }, {
        key: "_getGroupedListeners",
        value: function _getGroupedListeners() {
          var listenerGroups = [];
          var listenerGroupMap = [];
          var listeners = this._listeners;
  
          if (this._currentContext !== 'global') {
            listeners = [].concat(_toConsumableArray(listeners), _toConsumableArray(this._contexts.global.listeners));
          }
  
          listeners.sort(function (a, b) {
            return (b.keyCombo ? b.keyCombo.keyNames.length : 0) - (a.keyCombo ? a.keyCombo.keyNames.length : 0);
          }).forEach(function (l) {
            var mapIndex = -1;
  
            for (var i = 0; i < listenerGroupMap.length; i += 1) {
              if (listenerGroupMap[i] === null && l.keyCombo === null || listenerGroupMap[i] !== null && listenerGroupMap[i].isEqual(l.keyCombo)) {
                mapIndex = i;
              }
            }
  
            if (mapIndex === -1) {
              mapIndex = listenerGroupMap.length;
              listenerGroupMap.push(l.keyCombo);
            }
  
            if (!listenerGroups[mapIndex]) {
              listenerGroups[mapIndex] = [];
            }
  
            listenerGroups[mapIndex].push(l);
          });
          return listenerGroups;
        }
      }, {
        key: "_applyBindings",
        value: function _applyBindings(event) {
          var _this2 = this;
  
          var preventRepeat = false;
          event || (event = {});
  
          event.preventRepeat = function () {
            preventRepeat = true;
          };
  
          event.pressedKeys = this._locale.pressedKeys.slice(0);
          var activeTargetKeys = this._locale.activeTargetKeys;
          var pressedKeys = this._locale.pressedKeys.slice(0);
          var listenerGroups = this._getGroupedListeners();
  
          var _loop = function _loop(i) {
            var listeners = listenerGroups[i];
            var keyCombo = listeners[0].keyCombo;
  
            if (keyCombo === null || keyCombo.check(pressedKeys)) {
              for (var j = 0; j < listeners.length; j += 1) {
                var listener = listeners[j];
  
                if (!listener.executingHandler && listener.pressHandler && !listener.preventRepeat) {
                  listener.executingHandler = true;
                  listener.pressHandler.call(_this2, event);
                  listener.executingHandler = false;
  
                  if (preventRepeat || listener.preventRepeatByDefault) {
                    listener.preventRepeat = true;
                    preventRepeat = false;
                  }
                }
  
                if (_this2._appliedListeners.indexOf(listener) === -1) {
                  _this2._appliedListeners.push(listener);
                }
              }
  
              if (keyCombo) {
                for (var _j = 0; _j < keyCombo.keyNames.length; _j += 1) {
                  var index = pressedKeys.indexOf(keyCombo.keyNames[_j]);
  
                  if (index !== -1) {
                    pressedKeys.splice(index, 1);
                    _j -= 1;
                  }
                }
              }
            }
          };
  
          for (var i = 0; i < listenerGroups.length; i += 1) {
            _loop(i);
          }
        }
      }, {
        key: "_clearBindings",
        value: function _clearBindings(event) {
          event || (event = {});
          event.pressedKeys = this._locale.pressedKeys.slice(0);
  
          for (var i = 0; i < this._appliedListeners.length; i += 1) {
            var listener = this._appliedListeners[i];
            var keyCombo = listener.keyCombo;
  
            if (keyCombo === null || !keyCombo.check(this._locale.pressedKeys)) {
              listener.preventRepeat = false;
  
              if (keyCombo !== null || event.pressedKeys.length === 0) {
                this._appliedListeners.splice(i, 1);
  
                i -= 1;
              }
  
              if (!listener.executingHandler && listener.releaseHandler) {
                listener.executingHandler = true;
                listener.releaseHandler.call(this, event);
                listener.executingHandler = false;
              }
            }
          }
        }
      }, {
        key: "_handleCommandBug",
        value: function _handleCommandBug(event, platform) {
          // On Mac when the command key is kept pressed, keyup is not triggered for any other key.
          // In this case force a keyup for non-modifier keys directly after the keypress.
          var modifierKeys = ["shift", "ctrl", "alt", "capslock", "tab", "command"];
  
          if (platform.match("Mac") && this._locale.pressedKeys.includes("command") && !modifierKeys.includes(this._locale.getKeyNames(event.keyCode)[0])) {
            this._targetKeyUpBinding(event);
          }
        }
      }]);
  
      return Keyboard;
    }();
  
    function us(locale, platform, userAgent) {
      // general
      locale.bindKeyCode(3, ['cancel']);
      locale.bindKeyCode(8, ['backspace']);
      locale.bindKeyCode(9, ['tab']);
      locale.bindKeyCode(12, ['clear']);
      locale.bindKeyCode(13, ['enter']);
      locale.bindKeyCode(16, ['shift']);
      locale.bindKeyCode(17, ['ctrl']);
      locale.bindKeyCode(18, ['alt', 'menu']);
      locale.bindKeyCode(19, ['pause', 'break']);
      locale.bindKeyCode(20, ['capslock']);
      locale.bindKeyCode(27, ['escape', 'esc']);
      locale.bindKeyCode(32, ['space', 'spacebar']);
      locale.bindKeyCode(33, ['pageup']);
      locale.bindKeyCode(34, ['pagedown']);
      locale.bindKeyCode(35, ['end']);
      locale.bindKeyCode(36, ['home']);
      locale.bindKeyCode(37, ['left']);
      locale.bindKeyCode(38, ['up']);
      locale.bindKeyCode(39, ['right']);
      locale.bindKeyCode(40, ['down']);
      locale.bindKeyCode(41, ['select']);
      locale.bindKeyCode(42, ['printscreen']);
      locale.bindKeyCode(43, ['execute']);
      locale.bindKeyCode(44, ['snapshot']);
      locale.bindKeyCode(45, ['insert', 'ins']);
      locale.bindKeyCode(46, ['delete', 'del']);
      locale.bindKeyCode(47, ['help']);
      locale.bindKeyCode(145, ['scrolllock', 'scroll']);
      locale.bindKeyCode(188, ['comma', ',']);
      locale.bindKeyCode(190, ['period', '.']);
      locale.bindKeyCode(191, ['slash', 'forwardslash', '/']);
      locale.bindKeyCode(192, ['graveaccent', '`']);
      locale.bindKeyCode(219, ['openbracket', '[']);
      locale.bindKeyCode(220, ['backslash', '\\']);
      locale.bindKeyCode(221, ['closebracket', ']']);
      locale.bindKeyCode(222, ['apostrophe', '\'']); // 0-9
  
      locale.bindKeyCode(48, ['zero', '0']);
      locale.bindKeyCode(49, ['one', '1']);
      locale.bindKeyCode(50, ['two', '2']);
      locale.bindKeyCode(51, ['three', '3']);
      locale.bindKeyCode(52, ['four', '4']);
      locale.bindKeyCode(53, ['five', '5']);
      locale.bindKeyCode(54, ['six', '6']);
      locale.bindKeyCode(55, ['seven', '7']);
      locale.bindKeyCode(56, ['eight', '8']);
      locale.bindKeyCode(57, ['nine', '9']); // numpad
  
      locale.bindKeyCode(96, ['numzero', 'num0']);
      locale.bindKeyCode(97, ['numone', 'num1']);
      locale.bindKeyCode(98, ['numtwo', 'num2']);
      locale.bindKeyCode(99, ['numthree', 'num3']);
      locale.bindKeyCode(100, ['numfour', 'num4']);
      locale.bindKeyCode(101, ['numfive', 'num5']);
      locale.bindKeyCode(102, ['numsix', 'num6']);
      locale.bindKeyCode(103, ['numseven', 'num7']);
      locale.bindKeyCode(104, ['numeight', 'num8']);
      locale.bindKeyCode(105, ['numnine', 'num9']);
      locale.bindKeyCode(106, ['nummultiply', 'num*']);
      locale.bindKeyCode(107, ['numadd', 'num+']);
      locale.bindKeyCode(108, ['numenter']);
      locale.bindKeyCode(109, ['numsubtract', 'num-']);
      locale.bindKeyCode(110, ['numdecimal', 'num.']);
      locale.bindKeyCode(111, ['numdivide', 'num/']);
      locale.bindKeyCode(144, ['numlock', 'num']); // function keys
  
      locale.bindKeyCode(112, ['f1']);
      locale.bindKeyCode(113, ['f2']);
      locale.bindKeyCode(114, ['f3']);
      locale.bindKeyCode(115, ['f4']);
      locale.bindKeyCode(116, ['f5']);
      locale.bindKeyCode(117, ['f6']);
      locale.bindKeyCode(118, ['f7']);
      locale.bindKeyCode(119, ['f8']);
      locale.bindKeyCode(120, ['f9']);
      locale.bindKeyCode(121, ['f10']);
      locale.bindKeyCode(122, ['f11']);
      locale.bindKeyCode(123, ['f12']);
      locale.bindKeyCode(124, ['f13']);
      locale.bindKeyCode(125, ['f14']);
      locale.bindKeyCode(126, ['f15']);
      locale.bindKeyCode(127, ['f16']);
      locale.bindKeyCode(128, ['f17']);
      locale.bindKeyCode(129, ['f18']);
      locale.bindKeyCode(130, ['f19']);
      locale.bindKeyCode(131, ['f20']);
      locale.bindKeyCode(132, ['f21']);
      locale.bindKeyCode(133, ['f22']);
      locale.bindKeyCode(134, ['f23']);
      locale.bindKeyCode(135, ['f24']); // secondary key symbols
  
      locale.bindMacro('shift + `', ['tilde', '~']);
      locale.bindMacro('shift + 1', ['exclamation', 'exclamationpoint', '!']);
      locale.bindMacro('shift + 2', ['at', '@']);
      locale.bindMacro('shift + 3', ['number', '#']);
      locale.bindMacro('shift + 4', ['dollar', 'dollars', 'dollarsign', '$']);
      locale.bindMacro('shift + 5', ['percent', '%']);
      locale.bindMacro('shift + 6', ['caret', '^']);
      locale.bindMacro('shift + 7', ['ampersand', 'and', '&']);
      locale.bindMacro('shift + 8', ['asterisk', '*']);
      locale.bindMacro('shift + 9', ['openparen', '(']);
      locale.bindMacro('shift + 0', ['closeparen', ')']);
      locale.bindMacro('shift + -', ['underscore', '_']);
      locale.bindMacro('shift + =', ['plus', '+']);
      locale.bindMacro('shift + [', ['opencurlybrace', 'opencurlybracket', '{']);
      locale.bindMacro('shift + ]', ['closecurlybrace', 'closecurlybracket', '}']);
      locale.bindMacro('shift + \\', ['verticalbar', '|']);
      locale.bindMacro('shift + ;', ['colon', ':']);
      locale.bindMacro('shift + \'', ['quotationmark', '\'']);
      locale.bindMacro('shift + !,', ['openanglebracket', '<']);
      locale.bindMacro('shift + .', ['closeanglebracket', '>']);
      locale.bindMacro('shift + /', ['questionmark', '?']);
  
      if (platform.match('Mac')) {
        locale.bindMacro('command', ['mod', 'modifier']);
      } else {
        locale.bindMacro('ctrl', ['mod', 'modifier']);
      } //a-z and A-Z
  
  
      for (var keyCode = 65; keyCode <= 90; keyCode += 1) {
        var keyName = String.fromCharCode(keyCode + 32);
        var capitalKeyName = String.fromCharCode(keyCode);
        locale.bindKeyCode(keyCode, keyName);
        locale.bindMacro('shift + ' + keyName, capitalKeyName);
        locale.bindMacro('capslock + ' + keyName, capitalKeyName);
      } // browser caveats
  
  
      var semicolonKeyCode = userAgent.match('Firefox') ? 59 : 186;
      var dashKeyCode = userAgent.match('Firefox') ? 173 : 189;
      var equalKeyCode = userAgent.match('Firefox') ? 61 : 187;
      var leftCommandKeyCode;
      var rightCommandKeyCode;
  
      if (platform.match('Mac') && (userAgent.match('Safari') || userAgent.match('Chrome'))) {
        leftCommandKeyCode = 91;
        rightCommandKeyCode = 93;
      } else if (platform.match('Mac') && userAgent.match('Opera')) {
        leftCommandKeyCode = 17;
        rightCommandKeyCode = 17;
      } else if (platform.match('Mac') && userAgent.match('Firefox')) {
        leftCommandKeyCode = 224;
        rightCommandKeyCode = 224;
      }
  
      locale.bindKeyCode(semicolonKeyCode, ['semicolon', ';']);
      locale.bindKeyCode(dashKeyCode, ['dash', '-']);
      locale.bindKeyCode(equalKeyCode, ['equal', 'equalsign', '=']);
      locale.bindKeyCode(leftCommandKeyCode, ['command', 'windows', 'win', 'super', 'leftcommand', 'leftwindows', 'leftwin', 'leftsuper']);
      locale.bindKeyCode(rightCommandKeyCode, ['command', 'windows', 'win', 'super', 'rightcommand', 'rightwindows', 'rightwin', 'rightsuper']); // kill keys
  
      locale.setKillKey('command');
    }
  
    var keyboard = new Keyboard();
    keyboard.setLocale('us', us);
    keyboard.Keyboard = Keyboard;
    keyboard.Locale = Locale;
    keyboard.KeyCombo = KeyCombo;
  
    return keyboard;
  
  })));
// src/1-4.l2d.js
var L2D = {
    
    Loader: new Loader(),											// 资源加载器
	Keys: keyboardJS,												// 键盘声明
	Bindings: {},													// 声明脚本绑定
	Temporary: {},													// 声明脚本临时数据
	Interface: {},													// 声明脚本接口
	Storage: {},													// 声明储存仓库
};

String.prototype.padLeft = function (len, charStr) {
	var s = this + '';
	return new Array(len - s.length + 1).join(charStr,  '') + s;
}
String.prototype.padRight = function (len, charStr) {
	var s = this + '';
	return s + new Array(len - s.length + 1).join(charStr,  '');
}
// src/2-1.item.js
/**
 * 道具
 */
class Item {
    constructor() {
		this.amount = 1;
		this.maxAmount = 1;
		this.price = 100;
		this.effects = [];
    }

	clone() {
		const clone = new Item();
		for(var property in this)
			clone[property] = this[property];

		return clone;
	}

	/**
	 * 获取道具Icon
	 * @returns 返回的Icon
	 */
	getIcon() {
		return new PIXI.Sprite(new PIXI.Texture(PIXI.Texture.from(`res/${this.icon}`), {x:this.iconSrcX+1,y:this.iconSrcY+1,width:this.iconWidth-2,height:this.iconHeight-2 }));
	}

	/**
	 * 使用道具
	 * @param {Character} character 被应用的角色
	 */
	use(character) {
		// 检查是否消耗，如果消耗则从背包中移除
		if(this.consume)
			gloabl.bag.remove(this.url, 1);

		// 触发使用效果
		for(var i = 0; i < this.effects.length; i++)
			this.effects[i](character);
	}

	/**
	 * 应用装备给角色
	 * @param {Character} character 被应用的角色
	 */
	 apply(character, location) {
		// 获取已装备道具
		const equip = character.equipments[location];

		// 应用当前装备
		character.equipments[location] = this;
		
		// 如果存在已装备道具则脱下(重新计算属性)
		if(equip) {
			character.hp -= equip.hp;
			character.sp -= equip.sp;
			character.atk -= equip.atk;
			character.def -= equip.def;
			character.speed -= equip.speed;
			character.keep -= equip.keep;
			character.range -= equip.range;
			character.weight -= equip.weight;

			// 更新道具数量
			global.bag.apply(equip.url, -1);
		}
		
		// 重新计算角色属性
		character.hp += this.hp;
		character.sp += this.sp;
		character.atk += this.atk;
		character.def += this.def;
		character.speed += this.speed;
		character.keep += this.keep;
		character.range += this.range;
		character.weight += this.weight; 

		// 更新道具数量
		global.bag.apply(this.url, 1);

		// 触发装备效果
		for(var i = 0; i < this.effects.length; i++)
			this.effects[i](character);
	}

	/**
	 * 为道具创建使用或装备时效果
	 * @param {Action<character>} effectFunc 
	 */
	createEffect(effectFunc) {
		this.effects.push(effectFunc);
	}
}

function ItemLoader(loader) {
    this.loader = loader;
}
ItemLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, enity) {
        const jsonObj = JSON.parse(buffer);
		enity.name = jsonObj.name;
		enity.descrption = jsonObj.descrption;
		enity.price = jsonObj.price;
		enity.weight = jsonObj.weight;
		enity.maxAmount = jsonObj.maxAmount;

		const iconSplits = jsonObj.iconBounds.split(",");
		enity.iconSrcX = parseInt(iconSplits[0]);
		enity.iconSrcY = parseInt(iconSplits[1]);
		enity.iconWidth = parseInt(iconSplits[2]);
		enity.iconHeight = parseInt(iconSplits[3]);
		enity.icon = jsonObj.icon;

		// 读取自定义属性
		for(var i = 0; i < jsonObj.properties.length; i++) {
			const property = jsonObj.properties[i];
			enity[property.name] = property.value;
		}

		// 加载不同道具类型属性
		enity.itemType = jsonObj.itemType;
		switch(enity.itemType) {
			case 0:
				this.loadEqu(enity, jsonObj);
				break;
			case 1:
				this.loadUse(enity, jsonObj);
				break;
			default:
				this.loadOther(enity, jsonObj);
				break;
		}
		
		// 加载道具脚本
		currentItem = enity;
		window.require(enity.url + ".fs.js");

        return enity;
    },
	loadEqu(enity, json) {
		enity.location = json.equipment.location;
		enity.speed = json.equipment.speed;
		enity.keep = json.equipment.keep;
		enity.range = json.equipment.range;
		enity.hp = json.equipment.hp;
		enity.sp = json.equipment.sp;
		enity.atk = json.equipment.atk;
		enity.def = json.equipment.def;
	},
	loadUse(enity, json) {
		enity.consume = json.usable.consume;
		enity.cd = json.usable.cd;
		enity.limit = json.usable.limit;
		enity.target = json.usable.target;
	},
	loadOther(enity, json) {

	},
    create : function(url) {
        const item = new Item();
		item.url = url;
		return item;
    }
}

L2D.Loader.addMode("item", new ItemLoader(this));
// src/3-1.character.js
function updateEnityDirection(enity, hude) {
	const angle = 180 - Math.floor((hude * 180 / Math.PI) % 360);
	switch(true) {
		default: 
			enity.direction = 3;
			break;
		case angle > 45 && angle <= 135:
			enity.direction = 2;
			break;
		case angle > 135 && angle <= 225:
			enity.direction = 0;
			break;
		case angle > 225 && angle <= 315:
			enity.direction = 1;
			break;
	}
}

var characterAnimations = {};
function getCharacterAnimation(state) {
	if(!characterAnimations[state])
		characterAnimations[state] = L2D.Loader.load(`res/actions/${state}.ani`);
		
	return characterAnimations[state];
}

class Character extends PIXI.Container {
	constructor() {
		super();

		this._actions = [];

		this.size = 32;
		this.frame = 0;	
		this.direction = 0;
		this._lastState = "";
		this._lastFrameTime = new Date().getTime();

		this.state = `idea`;										// 初始待机动作
		this.isCylinder = true;										// 主角是一个圆柱体
		
		// 装备
		this.equipments = [];
		for(var i = 0; i < 5; i++)
			this.equipments.push(null);

		// 基础状态
		this.level = 1;
		this.exp = 0;
		this.hp = 100;
		this.sp = 100;
		this.atk = 20;
		this.def = 5;
		this.keep = 2000;
		this.range = 0.5;
		this.weight = 10;
		this.restitution = 0.6;
		this.speed = 1;
		this.acceleration = 1.2;
		this.aspd = 1.5;
	}
	
	moveTo(target) {
		const character = this;
		const path = this.scene.findPath(this.position, target);

		// 查找当前是否存在其他move动作
		for(var i = 0; i < character._actions.length; i++) {
			const action = character._actions[i];
			if(action.constructor.name == "MoveTo") {
				character._actions.splice(i, 1);
				break;
			}
		}

		return new Promise(function(resolve) { 
				const action = new MoveTo(character, path, function(action) {
				action.deleted=true;
				resolve();
			});
			character._actions.push(action);
			return action; 
		});
	}
	
	attack() {
		if(this.state == `attack` || this.state == `hit`)
			return;

		const character = this;
		return new Promise(function(resolve) { 
				const action = new Attack(character, function(action) {
				action.deleted=true;
				resolve();
			});
			character._actions.push(action);
			return action;
		});
	}
	
 	attackTo(target) {
		const character = this;

		// 查找当前是否存在其他move动作
		for(var i = 0; i < character._actions.length; i++) {
			const action = character._actions[i];
			if(action.constructor.name == "AttackTo") {
				character._actions.splice(i, 1);
				break;
			}
		}

		return new Promise(function(resolve) { 
				const action = new AttackTo(character, target, function(action) {
				action.deleted=true;
				resolve();
			});
			character._actions.push(action);
			return action;
		});
	}
	
	talkTo(target) {
		const character = this;
		return new Promise(function(resolve) { 
				const action = new TalkTo(character, target, function(action) {
				action.deleted=true;
				resolve();
			});
			character._actions.push(action);
			return action;
		});
	}

	hurt() {
		const character = this;
		return new Promise(function(resolve) { 
				const action = new Hurt(character, function(action) {
				action.deleted=true;
				resolve();
			});
			character._actions.push(action);
			return action;
		});
	}
	
	isEnemy(target) {
		return true;
	}
	
	get centerPoint() {
		return {x:this.x,y:this.y};
	}
	
	set centerPoint(val) {
		this.x = val.x;
		this.y = val.y;
	}
	
	get state() {
		return this._lastState;
	}
	 
	set state (state) {
		// 加载对应动画包
		if(this._lastState != state) {
			this._lastState = state;
			this.frame = -1;
			this.animation = getCharacterAnimation(state);
		}
	}
	
	update() { 

		// 更新角色动作
		for(var i = this._actions.length - 1; i >= 0; i--) {
			const action = this._actions[i];
			if(action.deleted) 
			 	this._actions.splice(i, 1);
			else {
				action.update();
				break;
			}
		}

		// 更新动画帧
		if(new Date().getTime() - this._lastFrameTime > 200) {
			this._lastFrameTime = new Date().getTime();
			this.frame++; 
			if(this.animation.Frames && 
				this.frame >= this.animation.Frames.length) {

				// 检查是否存在callback
				if(this.state == "hit") {
					// 定格动画
					this.frame = this.animation.Frames.length - 1;
				}
				else {
					// 否则是循环动画
					this.frame = 0; 
				}
			}
		}
		else return;
		
		// 更新当前目标
		if(!this.Properties) return;
		if(!this.animation || !this.animation.Frames) return;
		if(this.frame >= this.animation.Frames.length) return;
		
		var drawCalls = [];
		var frameInfo = this.animation.Frames[this.frame];
		for(var i = 0; i < frameInfo.Fragments.length; i++) {
		    var frag = frameInfo.Fragments[i];
		    drawCalls.push({
		        location : frag.Locations[this.direction],
		        fragment : frag
		    });
		}
		
		drawCalls.sort(function(a,b) { 
		    return b.location.Z - a.location.Z;
		}); 
		
		while(this.children.length > 0)
			this.removeChildAt(0);
			
		for(var i = 0; i < drawCalls.length; i++) {
		    var dc = drawCalls[i];
		    var style = this.Properties.Style[dc.fragment.Folder];
		    if(style) {
		        var width = 0;
		        switch(dc.fragment.Folder) {
		            case "adornment": width = 18; break;
		            case "armor": width = 32; break;
		            case "body": width = 32; break;
		            case "bodyback": width = 32; break;
		            case "hand": width = 16; break;
		            case "head": width = 32; break;
		            case "leg": width = 16; break;
		            case "shoulder": width = 32; break; 
		            case "weapon": width = 64; break;
		            default: width = 32; break;
		        }
				
		        var halfWidth = width / 2;
		        var cc = dc.location;
		        var url = "res/role/" + dc.fragment.Folder + "/" + style;
		        var texture = PIXI.Texture.from(url);
				
				const source = new PIXI.Texture(texture, { x:width * cc.Image, y:0, width:width, height:width });
				const bunny = new PIXI.Sprite(source);
				bunny.x = cc.X - halfWidth;
				bunny.y = cc.Y - halfWidth - this.direction * 72 - 8;
				bunny.scale = this.scale;
				bunny.angle = this.angle;
				this.addChild(bunny);
		    }
		}
	}
}

function CharacterLoader(loader) {
    this.loader = loader;
}
CharacterLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, enity) {
        var jsonObj = JSON.parse(buffer);
        for(var member in jsonObj)
			enity[member] = jsonObj[member];

        return enity;
    },
    create : function() {
        return new Character(); 
    }
}

function AnimationLoader(loader) {
    this.loader = loader;
}
AnimationLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, entry) {
        var jsonObj = JSON.parse(buffer);
        for(var member in jsonObj)
            entry[member] = jsonObj[member];

        return entry;
    },
    create : function() {
        return {};
    }
}

L2D.Loader.addMode("act", new CharacterLoader(this));
L2D.Loader.addMode("ani", new AnimationLoader(this));
// src/3-2.action.js
class Action {
}
// src/3-3.moveTo.js
class MoveTo extends Action {
	constructor(enity, path, callback) {
		super();
		
		this.enity = enity;
		this.path = path;
        this.callback = callback;
	}
	
	update() {
		if(!this.path || this.path.length <= 0) {
			this.enity.action = null;
			this.enity.state = "idea";
			return;
		}
		
		// 计算距离
		const target = this.path[0];
		const speed = this.enity.speed;
		const xDiff = target.x - this.enity.centerPoint.x;
		const yDiff = target.y - this.enity.centerPoint.y;
		const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
		
		if(distance < speed/2) {
			this.enity.state = "idea";
			this.enity.centerPoint = this.path[0];
			this.path.splice(0,1);
            this.callback(this);
		}
        else {
            const hude = xDiff == 0 && yDiff == 0 ? 0 : Math.atan2(xDiff, yDiff);
                    
            // 计算位置
            const x = speed*Math.sin(hude);
            const y = speed*Math.cos(hude);

            if(this.enity.scene.battle)
                Matter.Body.setVelocity(this.enity.body, {x,y});
            else {
                this.enity.centerPoint = {x:this.enity.centerPoint.x+x,y:this.enity.centerPoint.y+y};
                updateEnityDirection(this.enity, hude);

                if(this.enity.state != "attack") 
                    this.enity.state = distance > 2 ? "run":distance < 0.08 ?'idea':"walk";
            }
        }
	}
}
// src/3-4.talkTo.js
class TalkTo extends Action {
	constructor(enity, target, callback) {
		super();
		
		this.enity = enity;
		this.target = target;
        this.callback = callback;
	}
	
	update() {
		// 检查与目标点距离
		const xDiff = this.target.x - this.enity.x;
		const yDiff = this.target.y - this.enity.y;
		const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
		
		// 如果小于2个格子则进行对话
		if(distance < this.enity.scene.gridSize * 2) {

			// 结束动作
			this.enity.action = null;
			this.enity.state = `idea`;
			
			// 执行NPC对话
			if(this.target.script) 
				this.target.script(this.target);

            // 结束动作
            this.callback(this);
		}
		else {
			
			// 更新移动逻辑
			new MoveTo(enity, enity.scene.findPath(enity.position, target.position)).update();
		}	
	}
}
// src/3-5.attackTo.js
class AttackTo extends Action {
	constructor(enity, target, callback) {
		super();
		
		this.enity = enity;
		this.target = target;
        this.callback = callback;
	}
	
	async update() {
        // 如果自身死亡则停止动作
        if(this.enity.hp <= 0) {
            this.callback(this);
            return;
        }

		// 检查与目标点距离
		const xDiff = this.target.x - this.enity.x;
		const yDiff = this.target.y - this.enity.y;
		const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
		
		// 如果小于自身攻击范围的话进行攻击
		if(distance < this.enity.scene.gridSize * 2) {		
			// 执行计算面对朝向
			updateEnityDirection(this.enity, xDiff == 0 && yDiff == 0 ? 0 : Math.atan2(xDiff, yDiff));

			// 执行攻击动作
            await this.enity.attack();
		}
		else {
			// 更新移动逻辑
			new MoveTo(this.enity, this.enity.scene.findPath(this.enity.position, this.target.position)).update();
		}	
	}
}
// src/3-6.attack.js
/**
 * 直接攻击动作
 */
 class Attack extends Action {
	constructor(enity, callback) {
		super();

        this.callback = callback;
		this.enity = enity;
		this.enity.body.velocity.x *= 0.3;
		this.enity.body.velocity.y *= 0.3;
		Matter.Body.setVelocity(this.enity.body, this.enity.body.velocity);

		this.enity.state = "attack";
	}
	
	update() {
		if(this.enity.frame != 2)
			return;

		const sprite = this.enity;
		// 计算计算攻击范围和间隔
		const scene = sprite.scene;
		for(var i = 0; i < scene.children.length; i++) {
			const child = scene.children[i];
			// 如果不是角色则跳过
			if(!child.isCylinder || child.constructor.name!="Character" || (child.action && child.action.constructor.name=="Hurt"))
				continue;

			// 如果child不是攻击者
			if(child != sprite) {
				// 检查与目标点距离
				const xDiff = child.x - sprite.x;
				const yDiff = child.y - sprite.y;
				const distance = Math.sqrt(xDiff*xDiff+yDiff*yDiff);
				
				// 如果在攻击扇形内
				if(distance < sprite.scene.gridSize * 2) {		
					// 攻击成功
					Matter.Body.setVelocity(child.body, {x:xDiff*0.2,y:yDiff*0.2});

					// 受伤动画
					child.hurt();
				}
			}
		}

        this.callback(this);
        this.enity.state = "idea";
	}
}
// src/3-7.hurt.js
class Hurt extends Action {
	constructor(enity, callback) {
		super();

		this.enity = enity;
		this.enity.state = "hit";
        this.callback = callback;
	}

	update() {
		const speed = (Math.abs(this.enity.body.velocity.x) + Math.abs(this.enity.body.velocity.y)) / 2;
		if(speed<this.enity.speed/2) {
			this.enity.action = null;
			this.enity.state = "idea";
            this.callback(this);
		}
	}
}
// src/4-1.navmesh.js
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.NavMesh=n():t.NavMesh=n()}("undefined"!=typeof self?self:this,(function(){return(()=>{var t={774:(t,n)=>{var i,s,o,e;e=function(){function t(t){for(var n=t,i=[];n.parent;)i.unshift(n),n=n.parent;return i}var n={search:function(i,s,e,r){i.cleanDirty();var h=(r=r||{}).heuristic||n.heuristics.manhattan,c=r.closest||!1,a=new o((function(t){return t.f})),u=s;for(s.h=h(s,e),i.markDirty(s),a.push(s);a.size()>0;){var l=a.pop();if(l===e)return t(l);l.closed=!0;for(var p=i.neighbors(l),d=0,f=p.length;d<f;++d){var g=p[d];if(!g.closed&&!g.isWall()){var y=l.g+g.getCost(l),x=g.visited;(!x||y<g.g)&&(g.visited=!0,g.parent=l,g.h=g.h||h(g,e),g.g=y,g.f=g.g+g.h,i.markDirty(g),c&&(g.h<u.h||g.h===u.h&&g.g<u.g)&&(u=g),x?a.rescoreElement(g):a.push(g))}}}return c?t(u):[]},heuristics:{manhattan:function(t,n){return Math.abs(n.x-t.x)+Math.abs(n.y-t.y)},diagonal:function(t,n){var i=Math.sqrt(2),s=Math.abs(n.x-t.x),o=Math.abs(n.y-t.y);return 1*(s+o)+(i-2)*Math.min(s,o)}},cleanNode:function(t){t.f=0,t.g=0,t.h=0,t.visited=!1,t.closed=!1,t.parent=null}};function i(t,n){n=n||{},this.nodes=[],this.diagonal=!!n.diagonal,this.grid=[];for(var i=0;i<t.length;i++){this.grid[i]=[];for(var o=0,e=t[i];o<e.length;o++){var r=new s(i,o,e[o]);this.grid[i][o]=r,this.nodes.push(r)}}this.init()}function s(t,n,i){this.x=t,this.y=n,this.weight=i}function o(t){this.content=[],this.scoreFunction=t}return i.prototype.init=function(){this.dirtyNodes=[];for(var t=0;t<this.nodes.length;t++)n.cleanNode(this.nodes[t])},i.prototype.cleanDirty=function(){for(var t=0;t<this.dirtyNodes.length;t++)n.cleanNode(this.dirtyNodes[t]);this.dirtyNodes=[]},i.prototype.markDirty=function(t){this.dirtyNodes.push(t)},i.prototype.neighbors=function(t){var n=[],i=t.x,s=t.y,o=this.grid;return o[i-1]&&o[i-1][s]&&n.push(o[i-1][s]),o[i+1]&&o[i+1][s]&&n.push(o[i+1][s]),o[i]&&o[i][s-1]&&n.push(o[i][s-1]),o[i]&&o[i][s+1]&&n.push(o[i][s+1]),this.diagonal&&(o[i-1]&&o[i-1][s-1]&&n.push(o[i-1][s-1]),o[i+1]&&o[i+1][s-1]&&n.push(o[i+1][s-1]),o[i-1]&&o[i-1][s+1]&&n.push(o[i-1][s+1]),o[i+1]&&o[i+1][s+1]&&n.push(o[i+1][s+1])),n},i.prototype.toString=function(){for(var t=[],n=this.grid,i=0;i<n.length;i++){for(var s=[],o=n[i],e=0;e<o.length;e++)s.push(o[e].weight);t.push(s.join(" "))}return t.join("\n")},s.prototype.toString=function(){return"["+this.x+" "+this.y+"]"},s.prototype.getCost=function(t){return t&&t.x!=this.x&&t.y!=this.y?1.41421*this.weight:this.weight},s.prototype.isWall=function(){return 0===this.weight},o.prototype={push:function(t){this.content.push(t),this.sinkDown(this.content.length-1)},pop:function(){var t=this.content[0],n=this.content.pop();return this.content.length>0&&(this.content[0]=n,this.bubbleUp(0)),t},remove:function(t){var n=this.content.indexOf(t),i=this.content.pop();n!==this.content.length-1&&(this.content[n]=i,this.scoreFunction(i)<this.scoreFunction(t)?this.sinkDown(n):this.bubbleUp(n))},size:function(){return this.content.length},rescoreElement:function(t){this.sinkDown(this.content.indexOf(t))},sinkDown:function(t){for(var n=this.content[t];t>0;){var i=(t+1>>1)-1,s=this.content[i];if(!(this.scoreFunction(n)<this.scoreFunction(s)))break;this.content[i]=n,this.content[t]=s,t=i}},bubbleUp:function(t){for(var n=this.content.length,i=this.content[t],s=this.scoreFunction(i);;){var o,e=t+1<<1,r=e-1,h=null;if(r<n){var c=this.content[r];(o=this.scoreFunction(c))<s&&(h=r)}if(e<n){var a=this.content[e];this.scoreFunction(a)<(null===h?s:o)&&(h=e)}if(null===h)break;this.content[t]=this.content[h],this.content[h]=i,t=h}}},{astar:n,Graph:i}},"object"==typeof t.exports?t.exports=e():(s=[],void 0===(o="function"==typeof(i=e)?i.apply(void 0,s):i)||(t.exports=o))},636:(t,n,i)=>{"use strict";i.d(n,{default:()=>g});var s=i(774),o=i.n(s);class e{constructor(t=0,n=0){this.x=t,this.y=n}equals(t){return this.x===t.x&&this.y===t.y}angle(t){return Math.atan2(t.y-this.y,t.x-this.x)}distance(t){const n=t.x-this.x,i=t.y-this.y;return Math.sqrt(n*n+i*i)}add(t){this.x+=t.x,this.y+=t.y}subtract(t){this.x-=t.x,this.y-=t.y}clone(){return new e(this.x,this.y)}}class r{constructor(t,n){this.weight=1,this.x=0,this.y=0,this.id=t,this.polygon=n,this.edges=n.edges,this.neighbors=[],this.portals=[],this.centroid=this.calculateCentroid(),this.boundingRadius=this.calculateRadius()}getPoints(){return this.polygon.points}contains(t){return this.polygon.contains(t.x,t.y)||this.isPointOnEdge(t)}calculateCentroid(){const t=new e(0,0),n=this.polygon.points.length;return this.polygon.points.forEach((n=>t.add(n))),t.x/=n,t.y/=n,t}calculateRadius(){let t=0;for(const n of this.polygon.points){const i=this.centroid.distance(n);i>t&&(t=i)}return t}isPointOnEdge({x:t,y:n}){for(const i of this.edges)if(i.pointOnSegment(t,n))return!0;return!1}destroy(){this.neighbors=[],this.portals=[]}toString(){return`NavPoly(id: ${this.id} at: ${this.centroid})`}isWall(){return 0===this.weight}centroidDistance(t){return this.centroid.distance(t.centroid)}getCost(t){return this.centroidDistance(t)}}function h(t,n){const i=n.start,s=n.end,o=function(t,n){const i=n.x-t.x,s=n.y-t.y;return i*i+s*s}(i,s);let r=((t.x-i.x)*(s.x-i.x)+(t.y-i.y)*(s.y-i.y))/o;var h;return(h=r)<0&&(h=0),h>1&&(h=1),r=h,new e(i.x+r*(s.x-i.x),i.y+r*(s.y-i.y))}function c(t,n,i){const s=n.x-t.x,o=n.y-t.y;return(i.x-t.x)*o-s*(i.y-t.y)}function a(t,n,i=1e-4){return Math.abs(t-n)<=i}function u(t,n){let i=t-n;const s=i+Math.PI,o=2*Math.PI;return i=s-Math.floor(s/o)*o,i-=Math.PI,i}function l(t,n,i=1e-4){const s=c(t.start,t.end,n.start),o=c(t.start,t.end,n.end);return!(!a(s,0,i)||!a(o,0,i))}class p{constructor(){this.portals=[],this.path=[]}push(t,n){void 0===n&&(n=t),this.portals.push({left:t,right:n})}stringPull(){const t=this.portals,n=[];let i=0,s=0,o=0,e=t[0].left,r=t[0].left,h=t[0].right;n.push(e);for(var a=1;a<t.length;a++){const u=t[a].left,l=t[a].right;if(c(e,h,l)<=0){if(!(e.equals(h)||c(e,r,l)>0)){n.push(r),e=r,i=s,r=e,h=e,s=i,o=i,a=i;continue}h=l,o=a}if(c(e,r,u)>=0){if(!(e.equals(r)||c(e,h,u)<0)){n.push(h),e=h,i=o,r=e,h=e,s=i,o=i,a=i;continue}r=u,s=a}}return 0!==n.length&&n[n.length-1].equals(t[t.length-1].left)||n.push(t[t.length-1].left),this.path=n,n}}class d{constructor(t,n,i,s){this.start=new e(t,n),this.end=new e(i,s),this.left=Math.min(t,i),this.right=Math.max(t,i),this.top=Math.min(n,s),this.bottom=Math.max(n,s)}pointOnSegment(t,n){return t>=this.left&&t<=this.right&&n>=this.top&&n<=this.bottom&&this.pointOnLine(t,n)}pointOnLine(t,n){return(t-this.left)*(this.bottom-this.top)==(this.right-this.left)*(n-this.top)}}class f{constructor(t,n=!0){this.isClosed=n,this.points=t,this.edges=[];for(let n=1;n<t.length;n++){const i=t[n-1],s=t[n];this.edges.push(new d(i.x,i.y,s.x,s.y))}if(this.isClosed){const n=t[0],i=t[t.length-1];this.edges.push(new d(n.x,n.y,i.x,i.y))}}contains(t,n){let i=!1;for(let s=-1,o=this.points.length-1;++s<this.points.length;o=s){const e=this.points[s].x,r=this.points[s].y,h=this.points[o].x,c=this.points[o].y;(r<=n&&n<c||c<=n&&n<r)&&t<(h-e)*(n-r)/(c-r)+e&&(i=!i)}return i}}const g=class{constructor(t,n=0){this.meshShrinkAmount=n;const i=t.map((t=>{const n=t.map((t=>new e(t.x,t.y)));return new f(n)}));this.navPolygons=i.map(((t,n)=>new r(n,t))),this.calculateNeighbors(),this.graph=new class{constructor(t){this.grid=[],this.init=o().Graph.prototype.init.bind(this),this.cleanDirty=o().Graph.prototype.cleanDirty.bind(this),this.markDirty=o().Graph.prototype.markDirty.bind(this),this.toString=o().Graph.prototype.toString.bind(this),this.nodes=t,this.init()}neighbors(t){return t.neighbors}navHeuristic(t,n){return t.centroidDistance(n)}destroy(){this.cleanDirty(),this.nodes=[]}}(this.navPolygons)}getPolygons(){return this.navPolygons}destroy(){this.graph.destroy();for(const t of this.navPolygons)t.destroy();this.navPolygons=[]}isPointInMesh(t){return this.navPolygons.some((n=>n.contains(t)))}findClosestMeshPoint(t,n=Number.POSITIVE_INFINITY){let i=n,s=null,o=null;for(const n of this.navPolygons){if(n.contains(t)){i=0,s=n,o=t;break}const e=n.boundingRadius;if(n.centroid.distance(t)-e<i){const e=this.projectPointToPolygon(t,n);e.distance<i&&(i=e.distance,s=n,o=e.point)}}return{distance:i,polygon:s,point:o}}findPath(t,n){let i,s,r=null,h=null,c=Number.MAX_VALUE,a=Number.MAX_VALUE;const u=new e(t.x,t.y),l=new e(n.x,n.y);for(const t of this.navPolygons)s=t.boundingRadius,i=t.centroid.distance(u),i<=c&&i<=s&&t.contains(u)&&(r=t,c=i),i=t.centroid.distance(l),i<=a&&i<=s&&t.contains(l)&&(h=t,a=i);if(!h&&this.meshShrinkAmount>0)for(const t of this.navPolygons)if(s=t.boundingRadius+this.meshShrinkAmount,i=t.centroid.distance(l),i<=s){const{distance:n}=this.projectPointToPolygon(l,t);n<=this.meshShrinkAmount&&n<a&&(h=t,a=n)}if(!h)return null;if(!r&&this.meshShrinkAmount>0)for(const t of this.navPolygons)if(s=t.boundingRadius+this.meshShrinkAmount,i=t.centroid.distance(u),i<=s){const{distance:n}=this.projectPointToPolygon(u,t);n<=this.meshShrinkAmount&&n<c&&(r=t,c=n)}if(!r)return null;if(r===h)return[u,l];const d=o().astar.search(this.graph,r,h,{heuristic:this.graph.navHeuristic});if(0===d.length)return null;d.unshift(r);const f=new p;f.push(u);for(let t=0;t<d.length-1;t++){const n=d[t],i=d[t+1];let s=null;for(let t=0;t<n.neighbors.length;t++)n.neighbors[t].id===i.id&&(s=n.portals[t]);if(!s)throw new Error("Path was supposed to be found, but portal is missing!");f.push(s.start,s.end)}f.push(l),f.stringPull();let g=null;const y=[];for(const t of f.path){const n=t.clone();g&&n.equals(g)||y.push(n),g=n}return y}calculateNeighbors(){for(let t=0;t<this.navPolygons.length;t++){const n=this.navPolygons[t];for(let i=t+1;i<this.navPolygons.length;i++){const t=this.navPolygons[i];if(!(n.centroid.distance(t.centroid)>n.boundingRadius+t.boundingRadius))for(const i of n.edges)for(const s of t.edges){if(!l(i,s))continue;const o=this.getSegmentOverlap(i,s);if(!o)continue;n.neighbors.push(t),t.neighbors.push(n);const[e,r]=o;let h=n.centroid.angle(i.start),c=n.centroid.angle(o[0]),a=n.centroid.angle(o[1]),p=u(h,c),f=u(h,a);p<f?n.portals.push(new d(e.x,e.y,r.x,r.y)):n.portals.push(new d(r.x,r.y,e.x,e.y)),h=t.centroid.angle(s.start),c=t.centroid.angle(o[0]),a=t.centroid.angle(o[1]),p=u(h,c),f=u(h,a),p<f?t.portals.push(new d(e.x,e.y,r.x,r.y)):t.portals.push(new d(r.x,r.y,e.x,e.y))}}}}getSegmentOverlap(t,n){const i=[{line:t,point:t.start},{line:t,point:t.end},{line:n,point:n.start},{line:n,point:n.end}];i.sort((function(t,n){return t.point.x<n.point.x?-1:t.point.x>n.point.x?1:t.point.y<n.point.y?-1:t.point.y>n.point.y?1:0}));const s=i[0].line===i[1].line,o=i[1].point.equals(i[2].point);return s||o?null:[i[1].point,i[2].point]}projectPointToPolygon(t,n){let i=null,s=Number.MAX_VALUE;for(const o of n.edges){const n=h(t,o),e=t.distance(n);(null===i||e<s)&&(s=e,i=n)}return{point:i,distance:s}}}}},n={};function i(s){if(n[s])return n[s].exports;var o=n[s]={exports:{}};return t[s](o,o.exports,i),o.exports}return i.n=t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return i.d(n,{a:n}),n},i.d=(t,n)=>{for(var s in n)i.o(n,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},i.o=(t,n)=>Object.prototype.hasOwnProperty.call(t,n),i(636)})().default}));
//# sourceMappingURL=navmesh.js.map
// src/4-2.event.js
/**
 * 场景事件
 */
class SceneEvent {
	
}
// src/4-3.scene.js
class SceneImage extends PIXI.Container {
	 constructor(gridSize, scene) { 
		super(); 
		
		this.scene = scene;
		this.gridSize = gridSize;
	 }
	 update() {

		var alpha = 1;	
		if(this.scene.player && this.isCylinder)
			if(this.scene.player.x > this.x - 8 && this.scene.player.x < this.x + this.width &&
			   this.scene.player.y > this.y && this.scene.player.y < this.y + this.height - this.gridSize/2)
				alpha = alpha * 0.7;

		this.alpha = alpha;
	 }
	 
	 get centerPoint() {
		 return {x:this.x+this.width/2,y:this.y+this.height-this.gridSize/2};
	 }
	 
	 set centerPoint(val) {
	 	this.x = val.x - this.width/2;
	 	this.y = val.y - this.height+this.gridSize/2;
	 }
}

class Scene extends PIXI.Container {
	constructor() {
		super();
		this.images = [];
		this.checks = [];

		this._btnZ = new Button("z", {width:18,height:14});
	}

	createNpc(name, style, x, y, dir, func) {
		// 新建NPC
		var npc = L2D.Loader.load(`res/${style}`);
		// 为NPC添加动作
		npc.state = `idea`;
		// 为npcs设置坐标等信息
		npc.direction = dir;
		npc.x = x * currentScene.gridSize;
		npc.y = y * currentScene.gridSize;
		npc.scale = { x:1, y:1 };
		npc.angle = 0;
		npc.tint = {r:1,g:1,b:1};
		npc.isCylinder = true;
		npc.script = func;
		npc.name = name;

		// 添加到实体组件内
		this.addChild(npc);

		return npc;
	}

	createCheck(x, y, w, h, enter, leave) {
		this.checks.push({
			x: x * this.gridSize,
			y: y * this.gridSize,
			width: w * this.gridSize,
			height: h * this.gridSize,
			enter: enter,
			leave: leave
		});
	}
	
	update () {
		if(!this.checksum) return;
		
		// 绘制图形
		for(var i = 0; i < this.images.length; i++)
			this.images[i].update();

		// 检查进入checkpoint
		const x = player.centerPoint.x;
		const y = player.centerPoint.y;
		for(var i = 0; i < this.checks.length; i++) {
			const check = this.checks[i];
			if(x >= check.x && y >= check.y && x <= check.x + check.width && y <= check.y + check.height) {
				// enter
				if(check.entered) continue;

				check.entered = true;
				if(check.enter) check.enter();
			}
			else {
				// leave
				if(check.entered && check.leave) check.leave();

				check.entered = false;
			}
		}
		
		// 更新实体状态
		for(var i = 0; i < this.children.length; i++) {
			const sprite = this.children[i];
			if(sprite.update) sprite.update();
		}
		
		// 计算圆柱实体Z轴
		this.children.sort((a, b) => {
			if(a.isCylinder && b.isCylinder) {
				var aa = a.centerPoint;
				var bb = b.centerPoint;
				if (aa.y > bb.y) return 1;
				if (aa.y < bb.y) return -1;
				if (aa.x > bb.x) return 1;
				if (aa.x < bb.x) return -1;	
			} 
			
			if(a.isCylinder) return 1;
			if(b.isCylinder) return -1;
			return 0;
		});
		
		// 更新实体动作
		var zDis = 10000000;
		this.talker = null;
		for(var i = 0; i < this.children.length; i++) {			
			var enity = this.children[i];
			if(!enity.isCylinder) 
				continue;
					
			// 检查NPC交互
			if(!this.battle) {
				// 查找到距离最近NPC
				if(enity.constructor.name == "Character" && enity.script != null) {
					const xDiff = player.x - enity.x;
					const yDiff = player.y - enity.y;
					const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
					if(distance < this.gridSize * 2) {
						if(distance < zDis) {
							zDis = distance;
							this.talker = enity;
						}
					}
				}
			}
		}

		// 显示交互button
		if(this.talker != null && L2D.UIManager.children.length == 0) {
			this._btnZ.x = this.talker.centerPoint.x - this.gridSize / 2 * 3;
			this._btnZ.y = this.talker.centerPoint.y - this.gridSize / 2 * 3;
			this.addChild(this._btnZ);
		} else {
			this.removeChild(this._btnZ);
		}
		
		// 如果存在战场则更新战场
		if(this.battle) this.battle.update();
	}

	addChild (child) {
		super.addChild(child);
		child.scene = this;
	}

	/**
	 * 寻路
	 * @param {Object} src 当前坐标
	 * @param {Object} dst 目标坐标
	 */
	findPath(src, dst) {
		var path = this.navmesh.findPath(src, dst);
		if(path) path.splice(0,1);
		return path;
	}
	
	/**
	 * 开启战场
	 * @param {Object} loc 战场开启坐标
	 * @param {Object} radius 战场半径
	 */
	fight(loc, radius) {
		// 首先计算开战时范围内所有角色对主角的敌意
		const enemies = [];
		const childrens = [];
		for(var i = 0; i < this.children.length; i++) {
			const child = this.children[i];
			// 如果子项是圆柱体
			if(child.isCylinder) {
				// 计算与战场距离
				const xDiff = child.x - loc.x;
				const yDiff = child.y - loc.y;
				const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
				// 计算距离是否小于战场半径
				if(distance <= radius) {
					// 如果单位存在敌意则加入到战场敌人列表中
					if(child.isEnemy && child.isEnemy(player) && child != player)
						enemies.push(child);

					// 全部加入子集合以便后续触发战场事件
					childrens.push(child);
				}
			}
		}
		
		// 新建战场
		const battle = new Battle(this, enemies);
		// 触发战场事件
		//for(var i = 0; i < childrens.length; i++)
		//	childrens[i].dispatchEvent(new CustomEvent("battle"), { battle });
			
		// 赋予该场景战场属性
		this.battle = battle;
	}
}

function SceneLoader(loader) {
    this.loader = loader;
} 
SceneLoader.prototype = {
    responseType : 'text',
    load : function(buffer, params, enity) {
        var jsonObj = JSON.parse(buffer);

		enity.checksum = jsonObj.Inculde;
		enity.gridSize = jsonObj.GridSize;
		
		const texture = PIXI.Texture.from(`res/${jsonObj.Inculde}`);
		const columns = jsonObj.BitmapWidth / jsonObj.GridSize;
		// 添加地表
		const tileLayers = {};
		
		function getTileLayer(cell) {
			if(!tileLayers[0]) {
				const tiles = new PIXI.Container();
				tiles.zIndex = -1;
				enity.addChild(tiles);
				tileLayers[0] = tiles;				
			}
			
			return tileLayers[0];
		}
		
		var cellStartPosition = jsonObj.CellStartPosition.split(",");
		var cellStartX = parseInt(cellStartPosition[0]);
		var cellStartY = parseInt(cellStartPosition[1]);
		for(var x = 0; x < jsonObj.Cells.length; x++) {
		    const cellArr = jsonObj.Cells[x];
			for(y = 0; y < cellArr.length; y++) {
				const cell = cellArr[y];
				if(cell < 0)
					continue;

				const layer = getTileLayer(cell);
				
				const cellX = (x+cellStartX) * jsonObj.GridSize;
				const cellY = (y+cellStartY) * jsonObj.GridSize;
				const srcX = Math.floor(cell % columns) * jsonObj.GridSize + 1;
		    	const srcY = Math.floor(cell / columns) * jsonObj.GridSize + 1;
				
				// 绘制贴图
				const source = new PIXI.Texture(texture,  { x:srcX, y:srcY, width:jsonObj.GridSize-2, height:jsonObj.GridSize-2 });
				const bunny = new PIXI.Sprite(source);
				bunny.x = cellX;
				bunny.y = cellY;
				bunny.width = jsonObj.GridSize;
				bunny.height = jsonObj.GridSize;
				layer.addChildAt(bunny, 0);
			}
		}
				
		// 添加图形组件
		for(var i = 0; i < jsonObj.Images.length; i++) {
			var jsonImg = jsonObj.Images[i];
			const srcX = (jsonImg.Lower % columns) * jsonObj.GridSize;
			const srcY = Math.floor(jsonImg.Lower / columns) * jsonObj.GridSize;
			const dstX = (jsonImg.Upper % columns) * jsonObj.GridSize + jsonObj.GridSize;
			const dstY = Math.floor(jsonImg.Upper / columns) * jsonObj.GridSize + jsonObj.GridSize;
			const width = dstX - srcX;
			const height = dstY - srcY;
			
			var img = new SceneImage(jsonObj.GridSize, enity);
			
			const source = new PIXI.Texture(texture,  { x:srcX + 1, y:srcY + 1, width:width - 2, height:height - 2 });
			const bunny = new PIXI.Sprite(source);
			bunny.width = width;
			bunny.height = height;
			bunny.scale = { x:jsonImg.Transform.ScaleX, y:jsonImg.Transform.ScaleY };
			bunny.angle = jsonImg.Transform.Angle;
			bunny.tint = {r:jsonImg.Color.R,g:jsonImg.Color.G,b:jsonImg.Color.B};
			
			img.x = jsonImg.Transform.X * jsonObj.GridSize;
			img.y = jsonImg.Transform.Y * jsonObj.GridSize;
			img.width = width;
			img.heght = height;
			img.size = width;
			img.alpha = jsonImg.Color.A/255;
			img.isCylinder = jsonImg.IsCylinder;	
			img.addChild(bunny);
			enity.addChild(img);
		}
		
		// 添加阻挡区域
		var meshPolygonPoints = [];
		var blocks = [];
		var blockStartPosition = jsonObj.GridStartPosition.split(",");
		var blockStartX = parseInt(blockStartPosition[0]);
		var blockStartY = parseInt(blockStartPosition[1]);
		for(var yy = 0; yy < jsonObj.GridMap.length; yy++) {
			const row = jsonObj.GridMap[yy];
			for(var xx = 0; xx < row.length; xx++) {
				const x = (yy+blockStartX)*enity.gridSize;
				const y = (xx+blockStartY)*enity.gridSize;
				const w = enity.gridSize;
				const h = enity.gridSize;
					
				if(row[xx]) 
					meshPolygonPoints.push([{x,y},{x:x+w,y},{x:x+w,y:y+h},{x,y:y+h}]);
				else
					blocks.push({x,y,width:w,height:h});
			}
		}
		enity.blocks = blocks;
		enity.navmesh = new NavMesh(meshPolygonPoints);

		// 加载场景脚本
		currentScene = enity;
		window.require(enity.url + ".fs.js");

    },
	create : function(url) {
	    const scene = new Scene();  
		scene.url = url;
		return scene;
	}
};

L2D.Loader.addMode("gat", new SceneLoader(this));
// src/4-4.battle.js
/**
 * 战场
 * 战场会以当前场景为初始化
 */
class Battle {
	constructor(scene, enemies) {
		this.scene = scene;
		this.enemies = enemies;
		this.skills = [];
		
		// 新建碰撞引擎
		const engine = Matter.Engine.create();
		const render = Matter.Render.create({ element: app.view, engine:engine, options: {
            showVelocity: true,
            showAngleIndicator: true,
			width:640,
			height:360,
			background: 'transparent'
        }});
		
		engine.gravity.y = 0;
		engine.gravity.x = 0;
		
		// 将碰撞物体添加到引擎内
		const bodies = [];
		for(var i = 0; i < scene.children.length; i++) {
			const child = scene.children[i];
			if(child.isCylinder && child.constructor.name=="Character") {
				const cpts = child.centerPoint;
				child.body = Matter.Bodies.circle(cpts.x, cpts.y, child.size/4, 
				{
					isStatic:false, 
					mass:0.1,
					restitution:child.restitution,
					friction:0.05,
					frictionAir:0.0005
				});
				bodies.push(child.body);
			}
		}
		
		// 将阻挡物设置为静态
		for(var i = 0; i < scene.blocks.length; i++) {
			const block = scene.blocks[i];
			bodies.push(Matter.Bodies.rectangle(block.x+block.width/2, block.y+block.height/2, block.width, block.height, {isStatic:true}));
		}
		
		Matter.Composite.add(engine.world, bodies);// 将所有物体添加到世界中			
		//Matter.Render.run(render);//运行渲染器
		var runner = Matter.Runner.create();
		Matter.Runner.run(runner, engine);
		
		Matter.Render.lookAt(render, {
			min: { x: 0, y: 0 },
			max: { x: 480, y: 480 }
		});
		
		// 新建箭头
		const arrow = new PIXI.smooth.SmoothGraphics();
		arrow.beginFill(0xe1de81, 1);
		arrow.drawCircle(0, -30, 3);
		arrow.drawCircle(0, -42, 3);
		arrow.drawCircle(0, -54, 3);
		arrow.moveTo(0, -68).lineTo(-15, -64).lineTo(-13, -62).lineTo(0, -65).
			lineTo(13, -62).lineTo(15, -64).lineTo(0, -68);
		arrow.isCylinder = true;
		arrow.centerPoint = {x:arrow.x,y:arrow.y};
		
		// 添加碰撞事件
		/*
		Matter.Events.on(engine, 'collisionStart', function(e) {
			// 读取碰撞块
			for(var i = 0; i < e.pairs.length; i++) {
				const pair = e.pairs[i];
				const bodyA = pair.bodyA;
				const bodyB = pair.bodyB;
				
				if(bodyA.from.constructor.name == "AttackEffect") {
					
				} 
			}
		});
		*/

		// 将箭头加入场景
		this.scene.addChild(arrow);
		this.arrow = arrow;

		// 通知所有敌人去攻击主角
		for(var i = 0; i < this.enemies.length; i++)
			this.enemies[i].attackTo(this.scene.player);
	}
	
	/**
	 * 向战场添加技能
	 */
	skill(skill) {
		this.skills.push(skill);
	}
	
	/**
	 * 更新回合
	 * 再更新回合时会处理用户输入和游戏实体的AI
	 */
	update() {
		// 同步更新body
		for(var i = 0; i < scene.children.length; i++) {
			const child = scene.children[i];
			if(child.isCylinder && child.body) {
				const xDiff = child.body.position.x - child.centerPoint.x;
				const yDiff = child.body.position.y - child.centerPoint.y;
				child.centerPoint = {x:child.body.position.x,y:child.body.position.y};
				if(child.state != "hit") {
					const hude = Math.atan2(xDiff, yDiff);
					updateEnityDirection(child, hude);
				}

				if(child.state != "attack" && child.state != "hit") {
					const distance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
					child.state = distance > 2 ? "run":distance < 0.08 ?'idea':"walk";
				}
			}
		}
		
		// 同步技能
		for(var i = 0; i < this.skills.length; i++) {
			if(!this.skills[i].update()) {
				this.skills.splice(i);
				i--;
			}
		}
		
		// 让箭头跟随角色
		this.arrow.x = this.scene.player.x;
		this.arrow.y = this.scene.player.y - 8;
		
		// 计算箭头方向
		const xDiff = this.arrow.x - this.arrow.centerPoint.x;
		const yDiff = this.arrow.y - this.arrow.centerPoint.y;
		const hude = xDiff == 0 && yDiff == 0 ? 0 : Math.atan2(xDiff, yDiff);
		const angle = 180 - Math.floor((hude * 180 / Math.PI) % 360);
		this.arrow.angle = angle;	
		this.arrow.centerPoint = {x:this.arrow.x,y:this.arrow.y};
	}
	
	/**
	 * 战场结束
	 */
	end() {
		this.scene.removeChild(arrow);
		this.scene.battle = null;
	}
}
// src/5-1.uimanager.js
const tipsStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 10,
	fill: ['#778899'],
	stroke: '#181818',
	strokeThickness: 2
});

const smallStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 12,
	fontStyle: 'italic',
	fill: ['#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const bigStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 24,
	fill: ['#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const propertyStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 20,
	fill: ['#F8E800', '#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const nameStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 13,
	fill: ['#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const panelHeadStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 15,
	fontWeight: 'bold',
	fill: ['#384030'],
	stroke: '#181818',
	strokeThickness: 0
});

const textStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 16,
	fill: ['#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const selectedStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 16,
	fontWeight: 'bold',
	fill: ['#F8F8A0'],
	stroke: '#D85000',
	strokeThickness: 3
});

const unenableStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 16,
	fontWeight: 'bold',
	fill: ['#858584'],
	stroke: '#4A4A49',
	strokeThickness: 3
});

const spStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 16,
	fontStyle: 'italic',
	fontWeight: 'bold',
	fill: ['#78C8E8', '#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const hpStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 16,
	fontStyle: 'italic',
	fontWeight: 'bold',
	fill: ['#F8A898', '#FCFCFC'],
	stroke: '#181818',
	strokeThickness: 3
});

const talkStyle = new PIXI.TextStyle({
	fontFamily: 'Arial',
	fontSize: 14,
	fontWeight: 'bold',
	fill: ['#201000'],
	wordWrap: true,
	breakWords:true,
	wordWrapWidth: 240,
	lineJoin: 'round',
	dropShadow: true,
	dropShadowColor: '#D0C890',
	dropShadowBlur: 1,
	dropShadowAngle: Math.PI / 6,
	dropShadowDistance: 3,
});

const numberStyle = new PIXI.TextStyle({
	fontFamily: 'Arial',
	fontSize: 14,
	fill: ['#201000'],
	dropShadow: true,
	dropShadowColor: '#D0C890',
	dropShadowBlur: 1,
	dropShadowAngle: Math.PI / 6,
	dropShadowDistance: 2,
});

const numberSelectedStyle = new PIXI.TextStyle({
	fontFamily: 'Microsoft YaHei',
	fontSize: 13,
	fill: ['#F8F8A0'],
	stroke: '#D85000',
	strokeThickness: 2
});

const xStyle = new PIXI.TextStyle({
	fontFamily: 'Arial',
	fontSize: 16,
	fill: ['#585038'],
	dropShadow: true,
	dropShadowColor: '#C0C0A8',
	dropShadowBlur: 1,
	dropShadowAngle: Math.PI / 6,
	dropShadowDistance: 2,
});

class UIManager extends PIXI.Container {
	constructor() {
		super();
	}

	/**
	 * 当按下按钮时
	 */
	keyDown(key) {
		if(this._lastKey != key || (new Date().getTime() - this._lastKeyTime > 200)) {
			if(this.children.length > 0 && this.children[this.children.length - 1].keyDown)
				this.children[this.children.length - 1].keyDown(key);

			this._lastKey = key;
			this._lastKeyTime = new Date().getTime();
		}
	}

	/**
	 * 当更新时发生
	 */
	update() {
		for(var i = 0 ; i < this.children.length; i++)
			if(this.children[i].update)
				this.children[i].update();
	}
	
	/**
	 * 显示队伍界面
	 */
	party() {
		new Party(this);
	}

	/**
	 * 显示对话框
	 * @param {*} talker 
	 * @param {*} context 
	 * @param {*} option 
	 */
	talk (talker, context, option) {
		this.lastTalker = talker;
		this.lastContext = context;
		return new Promise(resolve => new Talk(this, talker, context, option, resolve));
	}
	
	/**
	 * 显示菜单,必须在talk后调用
	 * @param {*} items 
	 * @param {*} option 
	 */
	menu (items, option) {
		return new Promise(resolve => new TalkMenu(this, items, this.lastTalker, this.lastContext, option, resolve));
	}

	/**
	 * 开启指定物品商店
	 * @param {*} items 
	 * @param {*} option 
	 */
	buy (items, callback) {
		return new Promise(resolve => new Shop(this, {isSell:false,items,callback}, resolve));
	}

	/**
	 * 开启贩售商店
	 * @param {*} option 
	 */
	sell (callback) {
		return new Promise(resolve => new Shop(this, {isSell:true,callback}, resolve));
	}
}

L2D.UIManager = new UIManager();
L2D.UserInterface = new PIXI.Container();
// src/5-10.shop.js
class Shop extends PIXI.Container {
    constructor(uimanager, option, callback) {
        super();

        this.uimanager = uimanager;
        this.callback = callback;
        this.resolveAmout = 0;
        this.option = option || {isSell:true};

        // 添加底框
        const totalWidth = 160;
        const frame = new PIXI.smooth.SmoothGraphics();
        frame.beginFill(0x908058, 1);
        frame.lineStyle({ color: 0x2D2D2E, width: 1, alignment: 1 });
        frame.drawRoundedRect(0,0,totalWidth,24,20);
        frame.beginFill(0xE0E0C0, 1);
        frame.lineStyle({ color: 0xC0C0A8, width: 0, alignment: 1 });
        frame.drawRoundedRect(2,2,totalWidth-4,20,20);
        frame.endFill();
        frame.x = 410 - 160;
        frame.y = -36;
        this.addChild(frame);

        // 添加金币数
        const numLiteral = global.bag.gold+"$";
        this._gold = new PIXI.Text(numLiteral, numberStyle);
        this._gold.resolution = 2;
        this._gold.y = -32;
        this.addChild(this._gold);

        this.updateBag();
        this.addChild(global.bag);
        global.bag.selectOpation(0);

        this.x = (uimanager.realScreen.width - this.width) / 2;
        this.y = (uimanager.realScreen.height - this.height) / 2;
        uimanager.addChild(this);

        this._attach = global.bag;
    }

    updateBag() {
        const shop = this;

        shop._gold.text = global.bag.gold+"$";
        const numSize = PIXI.TextMetrics.measureText(shop._gold.text, numberStyle).width;
        shop._gold.x = 410 - numSize - 6;

        const tmp = global.bag.opationIndex;
        if(shop._numCtrl) {
            shop.removeChild(shop._numCtrl);
            shop._attach = global.bag;
        }

        function checkCanUse() {
            return true;
        }

        global.bag.setup(
            checkCanUse,
            function(item) {
                // 得到做大操作数量
                const maxAmount = shop.option.isSell ?
                    global.bag.exist(item.url) :
                    item.maxAmount - global.bag.exist(item.url);

                // 添加数量控件
                shop._numCtrl = new ShopNumber(shop.uimanager, shop, item, maxAmount, shop.option.isSell);
                shop._attach = shop._numCtrl;
                shop._numCtrl.x = shop.width - 100;
                shop._numCtrl.y = shop.height - 20;
                shop.addChild(shop._numCtrl);
            },
            function() {
                // 关闭商店
                shop.callback(shop.resolveAmout);
                shop.uimanager.removeChild(shop);
            }, false, true, this.option.isSell ? 0.5 : 1, this.option.items, tmp);
    }

    keyDown(key) {
        this._attach.keyDown(key);
    }
}

class ShopNumber extends PIXI.Container {
    constructor(uimanager, shop, item, opMaxAmount, isSell) {
        super();

        this.uimanager = uimanager;
        this.shop = shop;
        this.item = item;
        this.isSell = isSell;
        this.maxAmount = opMaxAmount;
        this._current = 0;
        this._index = 0;
        this._numbers = new PIXI.Container();

        // 计算宽度
        const maxAmount = item.maxAmount;
        const maxAmountString = maxAmount+"";
        this._singleSize = PIXI.TextMetrics.measureText("0", numberStyle).width;
        const totalWidth = this._singleSize * Math.max(2, maxAmountString.length) + 20;

        // 添加底框
		const frame = new PIXI.Graphics();
		frame.beginFill(0x908058, 1);
		frame.lineStyle({ color: 0x2D2D2E, width: 1, alignment: 1 });
		frame.drawRect(0,0,totalWidth,24);
		frame.beginFill(0xE0E0C0, 1);
		frame.lineStyle({ color: 0xC0C0A8, width: 0, alignment: 1 });
		frame.drawRect(2,2,totalWidth-4,20);
		frame.endFill();
        this.addChild(frame);

        // 添加x
        const literal = "×";
		const text = new PIXI.Text(literal, xStyle);
		text.resolution = 2;
		text.x = 5;
		text.y = 4;
		this.addChild(text);

        // 更新数字
        this.addChild(this._numbers);
        this.update();
        this.setNumber(1);
    }

    update() {
        const oldTexts = [];
        for(var i = 0; i < this._numbers.children.length; i++) {
            oldTexts.push(this._numbers.children[i].text);
            this._numbers.removeChild(this._numbers.children[i]);
            i--;
        }

        // 添加每个数字
        const currentAmountString = (this._current+"").padLeft(Math.max(2, (this.item.maxAmount+"").length), "0");
        var amountX = 15;
        for(var i = 0; i < currentAmountString.length; i++) {
            const isSelected = (currentAmountString.length - 1 - this._index) == i;
            const numLiteral = currentAmountString[i];
            const numText = new PIXI.Text(numLiteral, isSelected ? numberSelectedStyle : numberStyle);
            numText.resolution = 2;
            numText.x = amountX + (isSelected ? -1 : 0);
            numText.y = isSelected ? 3 : 4;
            numText.text = oldTexts[i] || "0";
            this._numbers.addChild(numText);

            amountX = amountX + this._singleSize;
        }
    }

    getNumber() {
        var numString = "";
        for(var i = 0; i < this._numbers.children.length; i++) {
            numString = numString + this._numbers.children[i].text;
        }

        return parseInt(numString);
    }

    setNumber(value) {
        value = Math.max(1, value);
        value = Math.min(this.maxAmount, value);

        const valueStr = (value+"").padLeft(Math.max(2, (this.item.maxAmount+"").length), "0");
        for(var i = 0; i < valueStr.length; i++) {
            this._numbers.children[i].text = valueStr[i]+"";
        }
    }

    keyDown(key) {
        switch(key) {
            case "left": 
                // 选择index
                this._index = Math.min(Math.max(2, (this.item.maxAmount+"").length) - 1, this._index + 1);
                this.update();
                break;
            case "right": 
                this._index = Math.max(0, this._index - 1);
                this.update();
                break;
            case "up":
                // 增加购买或贩售数量
                this.setNumber(this.getNumber() + parseInt("1".padRight(this._index + 1,"0")));
                break;
            case "down": 
                // 减少购买或贩售数量
                this.setNumber(this.getNumber() - parseInt("1".padRight(this._index + 1,"0")));
                break;
            case "z":
                // 隐藏背包
                this.uimanager.removeChild(this.shop);

                // 弹出售价和确认框
                const check = new ShopCheck(this.shop, this.uimanager, this.uimanager.lastTalker, this.item, this.getNumber(), this.isSell);
                check.x = this.uimanager.realScreen.width;
                check.y = 50;
                this.uimanager.addChild(check);
                createjs.Tween.get(check.position).to({x:this.uimanager.realScreen.width - 410,y:50},200);
                break;
            case "x":
                // 取消选框
                this.shop._attach = global.bag;
                this.shop.removeChild(this);
                break;
        }
    }
}

class ShopCheck extends PIXI.Container {
    constructor(shop, uimanager, talker, item, itemAmount, isSell) {
        super();

        this.item = item;
        this.itemAmount = itemAmount;
        this.shop = shop;
        this.uimanager = uimanager;
        this.amount = parseInt((item.price * itemAmount) / (isSell ? 2 : 1));
        this.isSell = isSell;

		// 创建对话框
		const frame = new PIXI.smooth.SmoothGraphics();
		frame.beginFill(0x686028, 1);
		frame.lineStyle({ color: 0x181818, width: 1, alignment: 0 });
		frame.drawRoundedRect(0,0,460,100,50);
		frame.beginFill(0xE0E0C0, 1);
		frame.lineStyle({ color: 0x686028, width: 0, alignment: 0 });
		frame.drawRoundedRect(2,2,460-4,100-4,50);
		frame.endFill();
        this.addChild(frame);

		// 设置对话位置	
		const richText = new PIXI.Text(`如果是这样,${(isSell ? "卖出" : "购买")}${item.name}×${itemAmount}\n就${this.amount}金币吧。`, talkStyle);
		richText.resolution = 2;
		richText.position.set(30,10);
		this.addChild(richText);	

		// 得到talker半身像
        if(talker) {
            const avater = PIXI.Texture.from(talker.avater);
            const spriteImage = new PIXI.Sprite(avater);
            spriteImage.x = 410 - 156;
            spriteImage.y = -50;
            this.addChild(spriteImage);
        }

        // 添加菜单
		this.menu = new Menu("MENU", 220);
		this.menu.addItem(new MenuItem("确定"));
		this.menu.addItem(new MenuItem("取消"));
        this.menu.selectedIndex = 0;
        this.menu.x = 400 - 220;
		this.menu.y = 116;
        this.addChild(this.menu);
    }

    async keyDown(key) {
        switch(key) {
            case "up": this.menu.up(); break;
            case "down": this.menu.down(); break;
            case "z":
                if(this.menu.selectedIndex == 0) {
                    // 如果是售卖
                    if(this.isSell) {
                        // 从背包中移除物品
                        global.bag.remove(this.item.url, this.itemAmount);

                        // 添加金币
                        global.bag.gold += this.amount;

                        // 播放金币到账声音

                    } else {
                        // 否则是购买，检查金币数量
                        if(global.bag.gold<this.amount) {
                            this.uimanager.removeChild(this);
                            // 金币数量不足提示
                            await L2D.UIManager.talk(L2D.UIManager.lastTalker, "你的金币不够哦。");
                        }
                        else {
                            // 向背包中添加物品
                            global.bag.add(this.item.url, this.itemAmount);

                            // 减少金币
                            global.bag.gold -= this.amount;

                            // 播放金币扣除声音
                        }
                    }

                    // 添加处理数量
                    this.shop.resolveAmout += this.itemAmount;
                    
                    // 调用callback
                    if(this.shop.option && this.shop.option.callback)
                        this.shop.option.callback(this.item.url, this.itemAmount);
                }
            case "x":
                // 更新背包
                this.shop.updateBag();
                // 返回上级菜单
                this.uimanager.addChild(this.shop);
                // 
                this.uimanager.removeChild(this);
                break;
        }
    }
}
// src/5-11.descrption.js
class Descrption extends PIXI.Container {
    constructor(uimanager) {
        super();
      
		this.uimanager = uimanager;

        const height = uimanager.realScreen.height;

		// 创建对话框
		const frame = new PIXI.smooth.SmoothGraphics();
		frame.beginFill(0x686028, 1);
		frame.lineStyle({ color: 0x181818, width: 1, alignment: 0 });
		frame.drawRoundedRect(0,0,260,height,25);
		frame.beginFill(0xE0E0C0, 1);
		frame.lineStyle({ color: 0x686028, width: 0, alignment: 0 });
		frame.drawRoundedRect(2,2,260-4,height-4,25);
		frame.endFill();
        
		this.addChild(frame);
        this.reset();
    }

    get visual() {
        return this._visual;
    }

    reset() {
        this._visual = false;
        this.x = this.uimanager.realScreen.width;
    }

    show(context) {

        if(this.richText)
            this.removeChild(this.richText);

		// 设置对话位置	
        const talkStyle = new PIXI.TextStyle({
            fontFamily: 'Arial',
            fontSize: 14,
            fontWeight: 'bold',
            fill: ['#201000'],
            wordWrap: true,
            breakWords:true,
            wordWrapWidth: 176,
            lineJoin: 'round',
            dropShadow: true,
            dropShadowColor: '#D0C890',
            dropShadowBlur: 1,
            dropShadowAngle: Math.PI / 6,
            dropShadowDistance: 3,
        });

		this.richText = new PIXI.Text(context, talkStyle);
		this.richText.x = 0;
		this.richText.y = 0;
		this.richText.resolution = 2;
		this.richText.position.set(10, 10);
	    this.addChild(this.richText);

        this.y = -this.parent.y + 25;

        this._visual = true;
        const target = this.uimanager.realScreen.width-this.width+25;
        createjs.Tween.get(this.position).to({x:target,y:this.y},(this.uimanager.realScreen.width - target) / 2);
    }

    close() {
        this._visual = false;
        createjs.Tween.get(this.position).to({x:this.uimanager.realScreen.width,y:this.y},(this.uimanager.realScreen.width - this.x) / 2);
    }
}
// src/5-12.talk.js
class Talk extends PIXI.Container {
	constructor(uimanager, talker, context, option, callback) {
		super();

		this.uimanager = uimanager;
		this.callback = callback;

		// 创建对话框
		const frame = new PIXI.smooth.SmoothGraphics();
		frame.beginFill(0x686028, 1);
		frame.lineStyle({ color: 0x181818, width: 1, alignment: 0 });
		frame.drawRoundedRect(0,0,460,100,50);
		frame.beginFill(0xE0E0C0, 1);
		frame.lineStyle({ color: 0x686028, width: 0, alignment: 0 });
		frame.drawRoundedRect(2,2,460-4,100-4,50);
		frame.endFill();

		frame.position.set(uimanager.realScreen.width - 410, 50);

		// 设置对话位置	
		const richText = new PIXI.Text(context, talkStyle);
		richText.x = 0;
		richText.y = 0;
		richText.resolution = 2;
		richText.position.set(frame.position.x + 30, frame.position.y + 10);

		// 得到talker半身像
		const avater = PIXI.Texture.from(talker.avater);
		const spriteImage = new PIXI.Sprite(avater);
		spriteImage.x = uimanager.realScreen.width - 156;

		// 添加按下button
		const button = new Button("z", {width:24,height:18}, 0xA4A484);
		button.x = uimanager.realScreen.width - 200;
		button.y = 128;
		
		// 添加一个对话框容器
		this.addChild(frame);
		this.addChild(richText);	
		this.addChild(spriteImage);	
		this.addChild(button);	
		this.uimanager.addChild(this);
	}

	end () {
		if (this.callback) this.callback(this);
		this.uimanager.removeChild(this);
	}

	keyDown(key) {
		if(key == "z") {
			this.end();
		}
	}
} 
// src/5-13.talkmenu.js
class TalkMenu extends PIXI.Container {
	constructor(uimanager, items, talker, context, option, callback) {
		super();
		this.uimanager = uimanager;
		this.callback = callback;
		this._itemLength = items.length;

		const talkStyle = new PIXI.TextStyle({
			fontFamily: 'Arial',
			fontSize: 14,
			fontWeight: 'bold',
			fill: ['#201000'],
			wordWrap: true,
			breakWords:true,
			wordWrapWidth: 240,
			lineJoin: 'round',
			dropShadow: true,
			dropShadowColor: '#D0C890',
			dropShadowBlur: 1,
			dropShadowAngle: Math.PI / 6,
			dropShadowDistance: 3,
		});

		// 创建对话框
		const frame = new PIXI.smooth.SmoothGraphics();
		frame.beginFill(0x686028, 1);
		frame.lineStyle({ color: 0x181818, width: 1, alignment: 0 });
		frame.drawRoundedRect(0,0,460,100,50);
		frame.beginFill(0xE0E0C0, 1);
		frame.lineStyle({ color: 0x686028, width: 0, alignment: 0 });
		frame.drawRoundedRect(2,2,460-4,100-4,50);
		frame.endFill();

		frame.position.set(uimanager.realScreen.width - 410, 50);

		// 设置对话位置	
		const richText = new PIXI.Text(context, talkStyle);
		richText.x = 0;
		richText.y = 0;
		richText.resolution = 2;
		richText.position.set(frame.position.x + 30, frame.position.y + 10);

		// 得到talker半身像
		const avater = PIXI.Texture.from(talker.avater);
		const spriteImage = new PIXI.Sprite(avater);
		spriteImage.x = uimanager.realScreen.width - 156;

		// 添加一个对话框容器
		this.addChild(frame);
		this.addChild(richText);	
		this.addChild(spriteImage);

		// 添加menu
		this.menu = new Menu("MENU", 210, 0, 170)
		this.menu.x = uimanager.realScreen.width - 220;
		this.menu.y = 166;
		this.addChild(this.menu);

		for(var i = 0; i < items.length; i++)
			this.menu.addItem(new MenuItem(items[i]));
		
		this.menu.selectedIndex = 0;
		this.uimanager.addChild(this);
	}

	keyDown(key) {
		switch(key) 
		{
			case "up": this.menu.up(); break;
			case "down": this.menu.down(); break;
			case "z":
				this.callback(this.menu.selectedIndex);
				this.uimanager.removeChild(this);
				break;
		}
	}
}

// src/5-2.background.js
class Background extends PIXI.Container {
	constructor(uimanager) {
		super();

		const width = uimanager.realScreen.width;
		const height = uimanager.realScreen.height;

		// 绘制背景
		const frameMask = new PIXI.Graphics();
		frameMask.beginFill(0x50A0B0, 1);
		frameMask.drawRect(0, 0, width, height);
		frameMask.endFill();
		this.addChild(frameMask);
		// 绘制菱形112x56
		const bgWidth = 56;
		const bgHeight = 28;
		const wn = width / bgWidth;
		const hn = height / bgHeight;

		for(var x = 0; x < wn; x++) {
			for(var y = 0; y < hn; y++) {
				const frame = new PIXI.Graphics();
				frame.beginFill(0x307890, 1);
				//frame.lineStyle({ color: 0xffffff, width: 1, alignment: 0 });
				frame.moveTo(bgWidth/2,  0).lineTo(bgWidth, bgHeight/2).lineTo(bgWidth/2, bgHeight).
					lineTo(0, bgHeight/2).lineTo(bgWidth/2, 0);

				const frameX = x * bgWidth;
				const frameY = y * bgHeight;
				frame.position.set(frameX, frameY);
				this.addChild(frame);
			}
		}
	}
}
// src/5-3.button.js
class Button extends PIXI.Container {
	constructor(text, size, lineStyle) {
		super();

		this.text = text;
		this.size = size;
		this.lineStyle = lineStyle || 0x284850;

		// 添加信息栏背景
		this.normal();
	}

	normal() {
		for(var i = 0; i < this.children.length; i++) {
			this.removeChild(this.children[i]);
			i--;
		}

		const infoBg = new PIXI.Graphics();
		infoBg.beginFill(0xA0C0B0, 1);
		infoBg.lineStyle({ color: this.lineStyle, width: 1, alignment: 0 });
		infoBg.drawRoundedRect(1,1,this.size.width,this.size.height,6);
		infoBg.lineStyle({ color: 0x181818, width: 0, alignment: 0 });
		infoBg.beginFill(0xC0F0E8, 1);
		infoBg.drawRoundedRect(1,1,this.size.width-2,this.size.height-2,6);
		infoBg.beginFill(0xFCFCFC, 1);
		infoBg.drawRoundedRect(2,2,this.size.width-4,this.size.height-4,6);
		infoBg.endFill();
		this.addChild(infoBg);

		// 绘制文字
		const textStyle = new PIXI.TextStyle({
			fontFamily: 'Microsoft YaHei',
			fontSize: 14,
			fontWeight: 'bold',
			fill: ['#181818'],
			stroke: '#000000',
			strokeThickness: 0
		});

		const literal = new PIXI.Text(this.text, textStyle);
		const literalSize = PIXI.TextMetrics.measureText(this.text, textStyle);
		literal.x = (this.size.width - literalSize.width) / 2;
		literal.y = (this.size.height - literalSize.height) / 2;
		literal.resolution = 2;
		this.addChild(literal);

		setTimeout(() => {
			this.pressed();
		}, 300);
	}

	pressed() {
		for(var i = 0; i < this.children.length; i++) {
			this.removeChild(this.children[i]);
			i--;
		}

		const infoBg = new PIXI.Graphics();
		infoBg.beginFill(0xA0C0B0, 1);
		infoBg.lineStyle({ color: this.lineStyle, width: 1, alignment: 0 });
		infoBg.drawRoundedRect(1,1,this.size.width,this.size.height,6);
		infoBg.lineStyle({ color: 0x181818, width: 0, alignment: 0 });
		infoBg.beginFill(0xC0F0E8, 1);
		infoBg.drawRoundedRect(1,1,this.size.width-2,this.size.height-2,6);
		infoBg.beginFill(0xFCFCFC, 1);
		infoBg.drawRoundedRect(3,3,this.size.width-6,this.size.height-6,6);
		infoBg.endFill();
		this.addChild(infoBg);

		// 绘制文字
		const textStyle = new PIXI.TextStyle({
			fontFamily: 'Microsoft YaHei',
			fontSize: 14,
			fontWeight: 'bold',
			fill: ['#181818'],
			stroke: '#000000',
			strokeThickness: 0
		});

		const literal = new PIXI.Text(this.text, textStyle);
		const literalSize = PIXI.TextMetrics.measureText(this.text, textStyle);
		literal.x = (this.size.width - literalSize.width + 1) / 2;
		literal.y = (this.size.height - literalSize.height + 1) / 2;
		literal.resolution = 2;
		this.addChild(literal);

		setTimeout(() => {
			this.normal();
		}, 300);
	}
}
// src/5-4.menu.js
class Menu extends PIXI.Container {
	constructor(panelName, width, minHeight, maxHeight) {
		super();

		this._visualY = 0;
		this._items = [];
		this._width = width;
		this._minHeight = minHeight ?? 0;
		this._maxHeight = maxHeight ?? minHeight ?? 99999;
		this.panelName = panelName;

		this.update();
	}

	get items() {
	 	return this._items;
	}

	get selected() {
		for(var i = 0; i < this.items.length; i++)
			if(this.items[i].selected)
				return this.items[i];
	}

	set selected(item) {
		for(var i = 0; i < this.items.length; i++)
			this.items[i].selected = false;

		if(item) item.selected = true;
	}

	get selectedIndex() {
		for(var i = 0; i < this.items.length; i++)
			if(this.items[i].selected)
				return i;

		return -1;
	}

	set selectedIndex(index) {
		for(var i = 0; i < this.items.length; i++)
			this.items[i].selected = i == index;
	}

	up() {
		this.selectedIndex = Math.max(0, this.selectedIndex - 1);

		// 取得显示范围
		if(this._maxHeight) {
			const index = this.selectedIndex;
			var visualY = 0;
			for(var i = 0; i < this.items.length; i++) {
				const item = this.items[i];
				if(index == i) {
					// 如果不在显示范围内
					if(visualY < this._visualY) {
						this._visualY = visualY;
						this.update();
						return;
					}
				}

				visualY = visualY + item.height + 1;
			}
		}
	}

	down() {
		this.selectedIndex = Math.min(this.items.length - 1, this.selectedIndex + 1);

		// 取得显示范围
		if(this._maxHeight) {
			const index = this.selectedIndex;
			var visualY = 24;
			for(var i = 0; i < this.items.length; i++) {
				const item = this.items[i];
				visualY = visualY + item.height + 1;
				if(index == i) {
					// 如果不在显示范围内
					if(visualY > this._visualY + this._maxHeight - 10) {
						this._visualY = Math.max(0, visualY - this._maxHeight + 11);
						this.update();
						return;
					}
				}
			}
		}
	}

	getItem(index) {
		return this._items[index];
	}

	getItemIndex(item) {
		for(var i = 0; i < this._items.length; i++) {
			if(this._items[i] == item)
				return i;
		}

		return -1;
	}

	addItem(item) {
		if(item.constructor.name != "MenuItem")
			throw "Menu child just support class MenuItem";

		this._items.push(item);
		this.update();
	}

	removeItem(item) {
		const index = this.getItemIndex(item);
		if(index>-1) {
			delete this._items[index];
			super.removeItem(item);
			this.update();
		}
	}

	clearItems() {	
        this._items = [];
		this._visualY = 0;
		this.update();
	}

	update() {
		for(var i = 0; i < this.children.length; i++) {
			super.removeChild(this.children[i]);
			i--;
		}

		var height = 24;
		for(var i = 0; i < this._items.length; i++) {
			const item = this._items[i];
			item.y = height - this._visualY;
			height = height + item.height + 1;
		}

		height = height + 10;
		if(height < this._minHeight)
			height = this._minHeight;

		if(this._maxHeight) {
			if(height > this._maxHeight)
				height = this._maxHeight;
		}

		const size = {width:this._width,height};

		// 绘制底板
		const frame = new PIXI.smooth.SmoothGraphics();
		frame.beginFill(0x0000FF, 0.5);
		frame.drawRect(0, 0, size.width, size.height);
		frame.endFill();
		super.addChild(frame);

		// 绘制底部
		const splitMask = new PIXI.smooth.SmoothGraphics();
		splitMask.beginFill(0xE8F880, 1);
		splitMask.drawRect(1, 1, size.width-2, 1);
		splitMask.endFill();

		const split = new PIXI.smooth.SmoothGraphics();
		split.beginFill(0xA8B848, 1);
		split.lineStyle({ color: 0x000000, width: 1, alignment: 0 });
		split.drawRect(0, size.height-8, size.width, 8);
		split.endFill();
		split.addChild(splitMask);
		super.addChild(split);

		// 绘制头部
		const headLiteral = this.panelName;
		const headText = new PIXI.Text(headLiteral, panelHeadStyle);
		headText.x = 20;
		headText.y = 2;
		headText.resolution = 2;

		const headMask = new PIXI.smooth.SmoothGraphics();
		headMask.lineStyle({ color: 0x707020, width: 1, alignment: 0 });
		headMask.beginFill(0xE8F880, 0);
		headMask.drawRect(1, 1, size.width-2, 19);
		headMask.endFill();

		const head = new PIXI.smooth.SmoothGraphics();
		head.beginFill(0xA8B848, 1);
		head.lineStyle({ color: 0x000000, width: 1, alignment: 0 });
		head.drawRect(0, 0, size.width, 21);
		head.endFill();
		head.addChild(headMask);
		head.addChild(splitMask);
		head.addChild(headText);
		super.addChild(head);

		for(var i = 0; i < this._items.length; i++) {
			const item = this._items[i];
			// 如果在显示范围内则添加
			if(item.y > 23 && item.y < this._maxHeight - 11) {
				super.addChild(item);
			}
		}
	}
}

class MenuItem extends PIXI.Container {
	constructor(text, callback) {
		super();

		this._text = text;
		this._selected = true;
		this._enable = true;
		this._buttonVisual = true;
		this.selected = false;
		this.callback = callback;
	}

	get enable() {
		return this._enable;
	}

	set enable(value) {
		if(this._enable!=value) {
			this._enable = value;
			this.update();
		}
	}

	get buttonVisual() {
		return this._buttonVisual;
	}

	set buttonVisual(value) {
		if(this.buttonVisual!=value) {
			this._buttonVisual=value;
			this.update();
		}
	}

	get connectContrl() {
		return this._connectContrl;
	}

	set connectContrl(value) {
		if(this._connectContrl!=value) {
			this._connectContrl=value;
			this.update();
		}
	}

	get icon() {
		return this._icon;
	}

	set icon(value) {
		if(this._icon != value) {
			this._icon = value;
			this.update();
		}
	}

	get selected() {
		return this._selected;
	}

	set selected(value) {
		if(this.selected != value) {
			this._selected = value;
			this.update();
		}
	}

	update() {
		for(var i = 0; i < this.children.length; i++) {
			this.removeChild(this.children[i]);
			i--;
		}

		// 添加icon
		var textX = 32;
		if(this.icon) {
			this.icon.x = textX;
			this.icon.y = 6;
			this.addChild(this.icon);
			textX += 20;
		}

		// 添加文字
		const literal = new PIXI.Text(this._text, this._enable ? this.selected ? selectedStyle : textStyle : unenableStyle);
		literal.x = textX;
		literal.y = 0;
		literal.resolution = 2;
		this.addChild(literal);

		// 添加按钮样式
		if(this.selected && this.buttonVisual) {
			const button = new Button("z", {width:21,height:16});
			button.x = 5;
			button.y = 4;
			button.enable = this.enable;
			this.addChild(button);
		}

		// 添加other
		if(this.connectContrl)
			this.addChild(this.connectContrl);
	}
}
// src/5-5.panel.js
class Panel extends PIXI.Container {
	constructor(panelName, size) {
		super();

		// 绘制底板
		const frame = new PIXI.smooth.SmoothGraphics();
		frame.beginFill(0x0000FF, 0.5);
		frame.drawRect(0, 0, size.width, size.height);
		frame.endFill();
		super.addChild(frame);

		// 绘制底部
		const splitMask = new PIXI.smooth.SmoothGraphics();
		splitMask.beginFill(0xE8F880, 1);
		splitMask.drawRect(1, 1, size.width-2, 1);
		splitMask.endFill();

		const split = new PIXI.smooth.SmoothGraphics();
		split.beginFill(0xA8B848, 1);
		split.lineStyle({ color: 0x000000, width: 1, alignment: 0 });
		split.drawRect(0, size.height-8, size.width, 8);
		split.endFill();
		split.addChild(splitMask);
		super.addChild(split);

		// 绘制头部
		const headLiteral = panelName;
		const headText = new PIXI.Text(headLiteral, panelHeadStyle);
		headText.x = 20;
		headText.y = 2;
		headText.resolution = 2;

		const headMask = new PIXI.smooth.SmoothGraphics();
		headMask.lineStyle({ color: 0x707020, width: 1, alignment: 0 });
		headMask.beginFill(0xE8F880, 0);
		headMask.drawRect(1, 1, size.width-2, 19);
		headMask.endFill();

		const head = new PIXI.smooth.SmoothGraphics();
		head.beginFill(0xA8B848, 1);
		head.lineStyle({ color: 0x000000, width: 1, alignment: 0 });
		head.drawRect(0, 0, size.width, 21);
		head.endFill();
		head.addChild(headMask);
		head.addChild(splitMask);
		head.addChild(headText);
		super.addChild(head);
	}

	addChild(child) {
		var y = 24;
		for(var i = 3;i < this.children.length; i++)
			y = y + this.children[i].height + 1;

		child.y = y;
		super.addChild(child);
	}
}

// src/5-6.inventory.js
class Inventory extends PIXI.Container {
	constructor(uimanager, index, location) {
		super();

		this.uimanager = uimanager;
		this._selectedIndex = -1;

		// 添加背景
		const bg = new Background(uimanager);
		this.addChild(bg);
		
		// 添加角色栏
		this.characterView = new PIXI.Container();
		const selector = new PIXI.smooth.SmoothGraphics();
		selector.beginFill(0xE4F27C, 0.8);
		selector.lineStyle({ color: 0x58D088, width: 1, alignment: 0 });
		selector.drawEllipse(0, 0, 64, 29);
		selector.endFill();
		selector.position={x:0,y:0};
		this.characterView.addChild(selector);
		this.characterView.x = location.x;
		this.characterView.y = location.y;
		this.addChild(this.characterView);

		// 选择角色
		this.selectCharacter(index);

		uimanager.addChild(this);
	}

	selectCharacter(index) {
		if(this._selectedIndex == index)
			return;

		this._selectedIndex = index;
		for(var i = 2; i < this.children.length; i++) {
			this.removeChild(this.children[i]);
			i--;
		}

		if(this.characterView.children.length > 1)
			this.characterView.removeChild(this.characterView.children[1]);

		const character = global.party[index];	
		character.state = "run";
		character.position={x:0,y:0};
		this.characterView.addChild(character);
		if(!this.characterViewTween) this.characterViewTween = createjs.Tween.get(this.characterView.position).to({x:480,y:72},500);

		// 添加头像信息栏
		this.info = new PIXI.Container();
		this.addChild(this.info);
		if(!this.infoTween) {
			this.info.position={x:-400,y:-20}
			this.infoTween = createjs.Tween.get(this.info.position).to({x:10,y:-20},500);
		} else {
			this.info.position={x:10,y:-20};
		}

		// 添加按钮
		this.leftButton = new Button("←", {width:24,height:18});
		this.leftButton.x = 400;
		this.leftButton.y = 122;
		this.info.addChild(this.leftButton);
		
		this.rightButton = new Button("→", {width:24,height:18});
		this.rightButton.x = 520;
		this.rightButton.y = 122;
		this.info.addChild(this.rightButton);

		// 添加当前index/total
		const length = `${this._selectedIndex+1}/${global.party.length}`;
		const literal = new PIXI.Text(length, textStyle);
		literal.x = 400 + 24 + 2 + (520 - 424 - PIXI.TextMetrics.measureText(length, textStyle).width) / 2;
		literal.y = 120;
		literal.resolution = 2;
		this.info.addChild(literal);

		// 添加信息栏半身像
		if(character.avater) {
			var texture = PIXI.Texture.from(character.avater);
			const avater = new PIXI.Sprite(texture);
			this.info.addChild(avater);
		}

		// 添加名称
		const nameLiteral = character.name || "unknown";
		const nameText = new PIXI.Text(nameLiteral, textStyle);
		nameText.x = 165;
		nameText.y = 40;
		nameText.resolution = 2;
		this.info.addChild(nameText);

		// HP/MP
		const hpHeadLiteral = "HP ";
		const mpHeadLiteral = "SP ";
		const hpHeadText = new PIXI.Text(hpHeadLiteral, hpStyle);
		const mpHeadText = new PIXI.Text(mpHeadLiteral, spStyle);
		hpHeadText.x = 165;
		mpHeadText.x = 165;
		hpHeadText.y = 67;
		mpHeadText.y = 87;
		hpHeadText.resolution = 2;
		mpHeadText.resolution = 2;
		this.info.addChild(hpHeadText);
		this.info.addChild(mpHeadText);

		const hpLiteral = character.hp;
		const mpLiteral = character.sp;
		const hpText = new PIXI.Text(hpLiteral, textStyle);
		const mpText = new PIXI.Text(mpLiteral, textStyle);
		hpText.x = 200;
		mpText.x = 200;
		hpText.y = 65;
		mpText.y = 85;
		hpText.resolution = 2;
		mpText.resolution = 2;
		this.info.addChild(hpText);
		this.info.addChild(mpText);

		// lv/exp
		const lvHeadLiteral = "lv.";
		const expHeadLiteral = "exp.";
		const lvHeadText = new PIXI.Text(lvHeadLiteral, smallStyle);
		const expHeadText = new PIXI.Text(expHeadLiteral, smallStyle);
		lvHeadText.x = 10+5;
		expHeadText.x = 50+5;
		lvHeadText.y = 126;
		expHeadText.y = 126;
		lvHeadText.resolution = 2;
		expHeadText.resolution = 2;
		this.info.addChild(lvHeadText);
		this.info.addChild(expHeadText);

		const lvLiteral = character.level;
		const expLiteral = character.exp;
		const lvText = new PIXI.Text(lvLiteral, textStyle);
		const expText = new PIXI.Text(expLiteral, textStyle);
		lvText.x = 24+5;
		expText.x = 74+5;
		lvText.y = 124;
		expText.y = 124;
		lvText.resolution = 2;
		expText.resolution = 2;
		this.info.addChild(lvText);
		this.info.addChild(expText);

		// 添加分割框
		const splitMask = new PIXI.smooth.SmoothGraphics();
		splitMask.beginFill(0xE8F880, 1);
		splitMask.drawRect(0, 0, this.uimanager.realScreen.width, 1);
		splitMask.endFill();
		splitMask.y = 1;

		this.split = new PIXI.smooth.SmoothGraphics();
		this.split.beginFill(0xA8B848, 1);
		this.split.lineStyle({ color: 0x000000, width: 1, alignment: 0 });
		this.split.drawRect(-1, 0, this.uimanager.realScreen.width + 2, 6);
		this.split.endFill();
		this.split.addChild(splitMask);
		this.addChild(this.split);
		if(!this.spiltTween) {
			this.spiltTween = createjs.Tween.get(this.split.position).to({x:0,y:124},500);
		} else {
			this.split.position={x:0,y:124};
		}

		// 添加底部状态描述
		this.updateData();

		// 添加菜单
		this.menu = new Menu("MENU", 180, 170);
		this.addChild(this.menu);
		if(!this.menuTween) {
			this.menu.x = this.uimanager.realScreen.width;
			this.menu.y = 155;
			this.menuTween = createjs.Tween.get(this.menu.position).to({x:395,y:155},500);
		} else {
			this.menu.x = 395;
			this.menu.y = 155;
		}

		// 添加选单到Panel内
		this.menu.addItem(new MenuItem("变更装备"));
		this.menu.addItem(new MenuItem("变更技能"));
		this.menu.addItem(new MenuItem("提升潜能"));
		this.menu.addItem(new MenuItem("战斗策略"));
		this.menu.addItem(new MenuItem("退出社团"));
		this.menu.selectedIndex = 0;
	}

    hideData() {
        this.removeChild(this.propertyGrid);
    }

    updateData() {
        this.removeChild(this.propertyGrid);
        
        const character = global.party[this._selectedIndex];	
        this.propertyAmount = 0;
        this.propertyGrid = new PIXI.Container();
		this.addProperty("atk", "攻击力", character.atk);
		this.addProperty("def", "防御力", character.def);
		this.addProperty("mov", "移动速度", character.speed);
		this.addProperty("weg", "负重", character.weight);
		this.addProperty("range", "攻击角度范围", character.range);
		this.addProperty("keep", "攻击持续时长", character.keep);
		this.addProperty("acc", "加速度", character.acceleration);
		this.addProperty("aspd", "最低攻击间隔", character.aspd);
        this.addChild(this.propertyGrid);
    }

    hideArrowButton() {
		this.info.removeChild(this.leftButton);
		this.info.removeChild(this.rightButton);
    }

    showArrowButton() {
        this.info.addChild(this.leftButton);
		this.info.addChild(this.rightButton);
    }

	addProperty(name, descrption, value) {
		
		const columnWidth = 120;

		const headLiteral = name;
		const headText = new PIXI.Text(headLiteral, propertyStyle);
		headText.resolution = 2;
		headText.x = 20 + (this.propertyAmount % 3) * columnWidth;
		headText.y = 160 + parseInt(this.propertyAmount / 3) * 48;
		this.propertyGrid.addChild(headText);

		const literal = value;
		const text = new PIXI.Text(literal, bigStyle);
		text.resolution = 2;
		text.x = 24 + PIXI.TextMetrics.measureText(headLiteral, propertyStyle).width + (this.propertyAmount % 3) * columnWidth;
		text.y = 158 + parseInt(this.propertyAmount / 3) * 48;
		this.propertyGrid.addChild(text);

		const descrptionLiteral = descrption;
		const descrptionText = new PIXI.Text(descrptionLiteral, tipsStyle);
		descrptionText.resolution = 2;
		descrptionText.x = 24 + (this.propertyAmount % 3) * columnWidth;
		descrptionText.y = 158 + parseInt(this.propertyAmount / 3) * 48 + 28;
		this.propertyGrid.addChild(descrptionText);

		this.propertyAmount++;
	}

    resetMenu() {
        createjs.Tween.get(this.menu.position).to({x:395,y:155},200);
    }

	keyDown(key) {
        const inventory = this;
		const uimanager = this.uimanager;

		switch(key) {
			case "left": this.selectCharacter(Math.max(0, this._selectedIndex - 1)); break;
			case "right": this.selectCharacter(Math.min(global.party.length - 1, this._selectedIndex + 1)); break;
			case "up": this.menu.up(); break;
			case "down": this.menu.down(); break;
			case "z": 
                createjs.Tween.get(this.menu.position).to({x:uimanager.realScreen.width,y:155},200).call(function(e) {
                    var overlap;
                    switch(inventory.menu.selectedIndex) {
                        case 0:
                            overlap = new Equipment(global.party[inventory._selectedIndex], uimanager, inventory);
                            break;
                    }
                    uimanager.addChild(overlap);
                });
				break;
			case "x":
				this.spiltTween = createjs.Tween.get(this.split.position).to({x:this.split.position.x,y:-this.split.height},300).call(function(e) {
					uimanager.removeChild(uimanager.children[uimanager.children.length - 1]);
					uimanager.children[uimanager.children.length - 1].reset();
				});
				this.menuTween = createjs.Tween.get(this.menu.position).to({x:this.menu.position.x,y:-this.menu.height},300);
				this.infoTween = createjs.Tween.get(this.info.position).to({x:this.info.position.x,y:-this.info.height},300);
				this.infoTween = createjs.Tween.get(this.propertyGrid.position).to({x:this.propertyGrid.position.x,y:-this.propertyGrid.height},150);
				this.infoTween = createjs.Tween.get(this.characterView.position).to({x:this.characterView.position.x,y:-this.characterView.height},250);
				break;
		}
	}
}

// src/5-6.party.js
class Party extends PIXI.Container {
	constructor(uimanager) {
		super();

		this.test();
		this.uimanager = uimanager;
		this.maxColumn = 5;

		this._selectedIndex = -1;

		// 添加背景
		const bg = new Background(uimanager);
		this.addChild(bg);

		// 添加选择栏
		this.selector = new PIXI.smooth.SmoothGraphics();
		this.selector.beginFill(0xE4F27C, 0.8);
		this.selector.lineStyle({ color: 0x58D088, width: 1, alignment: 0 });
		this.selector.drawEllipse(0, 0, 64, 29);
		this.selector.endFill();
		this.selector.position=this.getLocation(0);
		this.addChild(this.selector);

		// 添加队伍角色
		for(var i = 0; i < global.party.length; i++) {
			const character = global.party[i];
			// 添加角色
			character.centerPoint = this.getLocation(i);
			this.addChild(character);

			// 添加角色名称
			const nameLiteral = character.name || "unknown";
			const nameText = new PIXI.Text(nameLiteral, nameStyle);
			nameText.x = character.centerPoint.x + -PIXI.TextMetrics.measureText(nameLiteral, nameStyle).width / 2;
			nameText.y = character.centerPoint.y;
			nameText.resolution = 2;
			this.addChild(nameText);
		}

		uimanager.addChild(this);
		this.select(0);
	}

	getLocation(index) {
		const intRow = parseInt(index / this.maxColumn);
		const x = index % this.maxColumn * 96 + (intRow % 2 == 0 ? 130 : 80);
		const y = intRow * 70 + 42;
		return {x,y};
	}

	select(index) {
		if(this._selectedIndex == index)
			return;

		this._selectedIndex = index;

		// 如果存在之前的信息栏则移除
		if(this.info)
			this.removeChild(this.info);

		// 得到选择的character
		const character = global.party[index];

		// 添加信息栏
		this.info = new PIXI.Container();
		this.info.position={x:0,y:this.uimanager.realScreen.height-120}
		this.addChild(this.info);

		// 添加信息栏背景
		var infoBg = new PIXI.smooth.SmoothGraphics();
		infoBg.beginFill(0x0000FF, 0.5);
		infoBg.drawRoundedRect(-100, 30, 430, 200, 100);
		infoBg.endFill();
		this.info.addChild(infoBg);

		// 添加信息栏半身像
		if(character.avater) {
			var texture = PIXI.Texture.from(character.avater);
			const avater = new PIXI.Sprite(texture);
			this.info.addChild(avater);
		}

		// 添加名称
		const nameLiteral = character.name || "unknown";
		const nameText = new PIXI.Text(nameLiteral, textStyle);
		nameText.x = 165;
		nameText.y = 40;
		nameText.resolution = 2;
		this.info.addChild(nameText);

		// HP/MP
		const hpHeadLiteral = "HP ";
		const mpHeadLiteral = "SP ";
		const hpHeadText = new PIXI.Text(hpHeadLiteral, hpStyle);
		const mpHeadText = new PIXI.Text(mpHeadLiteral, spStyle);
		hpHeadText.x = 165;
		mpHeadText.x = 165;
		hpHeadText.y = 67;
		mpHeadText.y = 87;
		hpHeadText.resolution = 2;
		mpHeadText.resolution = 2;
		this.info.addChild(hpHeadText);
		this.info.addChild(mpHeadText);

		const hpLiteral = character.hp;
		const mpLiteral = character.sp;
		const hpText = new PIXI.Text(hpLiteral, textStyle);
		const mpText = new PIXI.Text(mpLiteral, textStyle);
		hpText.x = 200;
		mpText.x = 200;
		hpText.y = 65;
		mpText.y = 85;
		hpText.resolution = 2;
		mpText.resolution = 2;
		this.info.addChild(hpText);
		this.info.addChild(mpText);

		// lv/exp
		const lvHeadLiteral = "lv.";
		const expHeadLiteral = "exp.";
		const lvHeadText = new PIXI.Text(lvHeadLiteral, smallStyle);
		const expHeadText = new PIXI.Text(expHeadLiteral, smallStyle);
		lvHeadText.x = 10+5;
		expHeadText.x = 50+5;
		lvHeadText.y = 102;
		expHeadText.y = 102;
		lvHeadText.resolution = 2;
		expHeadText.resolution = 2;
		this.info.addChild(lvHeadText);
		this.info.addChild(expHeadText);

		const lvLiteral = character.level;
		const expLiteral = character.exp;
		const lvText = new PIXI.Text(lvLiteral, textStyle);
		const expText = new PIXI.Text(expLiteral, textStyle);
		lvText.x = 24+5;
		expText.x = 74+5;
		lvText.y = 100;
		expText.y = 100;
		lvText.resolution = 2;
		expText.resolution = 2;
		this.info.addChild(lvText);
		this.info.addChild(expText);

		// 播放选择器动画
		this.selectorTween = createjs.Tween.get(this.selector.position).to(this.getLocation(index),200);
		this.info.position.x = -400;
		this.infoTween = createjs.Tween.get(this.info.position).to({x:0,y:this.info.position.y},200);
	}

	keyDown(key) {
		var column = this._selectedIndex % this.maxColumn;
		var row = parseInt(this._selectedIndex / this.maxColumn);

		switch(key) {
			case "left": column = Math.max(0, column - 1); break;
			case "right": column = Math.min(this.maxColumn - 1, column + 1); break;
			case "up": row = Math.max(0, row - 1); break;
			case "down": row = Math.min(parseInt(global.party.length / this.maxColumn), row + 1); break;
			case "z": this.enity(this._selectedIndex); return;
			case "x": this.uimanager.removeChild(this.uimanager.children[this.uimanager.children.length - 1]); return;
		}

		var targetIndex = column + row * this.maxColumn;
		if(targetIndex < 0) targetIndex = 0;
		if(targetIndex >= global.party.length) targetIndex = global.party.length - 1;
		this.select(targetIndex);
	}

	update() {
		for(var i = 0; i < global.party.length; i++)
			global.party[i].update();
	}

	test() {
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/test.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/test.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/test.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/test.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/test.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
		global.party.push(L2D.Loader.load("res/npc.act"));
	}

	enity(index) {
		new Inventory(this.uimanager, index, this.getLocation(index));
	}

	reset() {
		const info = this.children[this.children.length - 1];
		this.removeChild(info);

		for(var i = 0; i < global.party.length; i++) {
			const character = global.party[i];
			character.state = "idea";
			character.centerPoint = this.getLocation(i);
			this.addChild(character);
		}

		this.addChild(info);
		this.info.position.x = -400;
		this.infoTween = createjs.Tween.get(this.info.position).to({x:0,y:this.info.position.y},200);
	}
}
// src/5-7.equipment.js
class Equipment extends PIXI.Container {
    constructor(character, uimanager, inventory) {
        super();
        this.character = character;
        this.uimanager = uimanager;
        this.inventory = inventory;
        this.inventory.hideArrowButton();

        // 添加菜单
		this.menu = new Menu("EQUIP", 220, 170);
		this.addChild(this.menu);
		if(!this.menuTween) {
			this.menu.x = this.uimanager.realScreen.width;
			this.menu.y = 155;
			this.menuTween = createjs.Tween.get(this.menu.position).to({x:395,y:155},200);
		} else {
			this.menu.x = 395;
			this.menu.y = 155;
		}

		// 添加选单到Panel内
        this.updateMenu();
    }

    updateMenu() {
        var tmp = this.menu.selectedIndex;
        this.menu.clearItems();

        for(var i = 0; i < this.character.equipments.length; i++) {
            var equip = this.character.equipments[i];
            if(equip) {
                const menuItem = new MenuItem(equip.name);
                menuItem.icon = equip.getIcon();
                menuItem.tag = equip;
                this.menu.addItem(menuItem);
            }
            else {
                this.menu.addItem(new MenuItem("-"));
            }
        }

        if(tmp<0) tmp = 0;
        this.menu.selectedIndex = tmp;
    }

    reset() {
        this._inHiding = false;
        this.menu.selected.buttonVisual = true;
        this.menuTween = createjs.Tween.get(this.menu.position).to({x:395,y:155},200);
    }

    keyDown(key) {
        switch(key) {
            case "up": this.menu.up(); break;
			case "down": this.menu.down(); break;
            case "z":
                if(!this._inHiding) {
                    this._inHiding = true;
                    const uimanager = this.uimanager;
                    const equipment = this;

                    // 隐藏按钮
                    this.menu.selected.buttonVisual = false;
                    // 隐藏数值栏
                    this.inventory.hideData();

                    // 检查物品是否可装备
                    function checkCanUse(item) {
                        // location 3 可以装备2个
                        // 得到当前角色locations
                        const locations = [];
                        for(var i = 0; i < 5; i++)
                            locations.push(0);

                        for(var i = 0; i < equipment.character.equipments.length; i++) {
                            var equip = equipment.character.equipments[i];
                            if(equip && i != equipment.menu.selectedIndex) {
                                locations[equip.location]++;
                            }
                        }

                        if(locations[item.location] < 1 || (item.location == 3 && locations[3] < 2)) 
                            return true;

                        return false;
                    }

                    global.bag.setup(
                       checkCanUse,
                        function(item) {
                            if(!checkCanUse(item)) {
                                // TODO 播放停止声

                                return;
                            }

                            // 按下z装备道具
                            item.apply(equipment.character, equipment.menu.selectedIndex);
                            // 更新道具栏
                            equipment.updateMenu();
                            // 更新数值
                            equipment.inventory.updateData();
                            // 恢复状态
                            equipment.reset();
                            uimanager.removeChild(global.bag);
                            // TODO 播放成功声
                        },
                        function() {
                            equipment.updateMenu();
                            equipment.inventory.updateData();
                            equipment.reset();
                            uimanager.removeChild(global.bag);
                        });

                    this.menuTween = createjs.Tween.get(this.menu.position).to({x:this.uimanager.realScreen.width-130,y:155},200).call(function() {
                        global.bag.x = 60;
                        global.bag.y = 155;
                        uimanager.addChild(global.bag);
                    });
                }
                break;
            case "x":
                if(!this._inHiding) {
                    this._inHiding = true;
                    const uimanager = this.uimanager;
                    const inventory = this.inventory;
                    createjs.Tween.get(this.position).to({x:uimanager.realScreen.width,y:0},200).call(function() {
                        uimanager.removeChild(uimanager.children[uimanager.children.length - 1]);
                        inventory.resetMenu();
                        inventory.showArrowButton();
                    });
                }
                break;
        }
    }


}
// src/5-8.bag.js
/**
 * 包裹
 */
 class Bag extends PIXI.Container {
	constructor() {
		super();
		this.items = {};
		this.amounts = {};
		this.applys = {};
		this.gold = 0;
		this._opationIndex = -1;

		// 添加菜单
		this.menu = new Menu("ITEM", 420, 170);
		this.addChild(this.menu);

		// 顶部选单
		this.opations = new PIXI.Container();
		this.addChild(this.opations);

		// 添加左右按钮
		const leftButton = new Button("←", {width:24,height:18});
		leftButton.x = 70;
		leftButton.y = -4;
		this.addChild(leftButton);
		
		this.rightButton = new Button("→", {width:24,height:18});
		this.rightButton.x = 248;
		this.rightButton.y = -4;
		this.addChild(this.rightButton);
	}

	test() {
		function createTestItem(index) {
			const item = new Item();
			item.name = "测试道具" + index;
			item.itemType = 0;
			item.location = 0;
			item.url = "w"+index;
			return item;
		}

		function createTestItem2(index) {
			const item = new Item();
			item.name = "测试道具" + index;
			item.itemType = 0;
			item.location = 1;
			item.url = "a"+index;
			return item;
		}

		for(var i = 0; i < 10; i++) {
			this.items["w"+i] = createTestItem(i);
			this.amounts["w"+i] = 1;
			this.applys["w"+i] = 0;
		}

		for(var i = 0; i < 10; i++) {
			this.items["a"+i] = createTestItem2(i);
			this.amounts["a"+i] = 1;
			this.applys["a"+i] = 0;
		}
	}

	get opationIndex() {
		return this._opationIndex;
	}

	hideButtons() {
		
	}

	addOpation(icon, location, isSelected) {
		
		const c = new PIXI.Container();
		const x = this.opations.children.length * 24;

		c.location = location;
		c.x = 100 + x;
		c.y = -4;

		// 选框
		const box = new PIXI.smooth.SmoothGraphics();
		box.beginFill(isSelected ? 0xC8C090 : 0x889858, 1);
		box.lineStyle({ color: isSelected ? 0xF0F0B8 : 0x587038, width: 2, alignment: 0 });
		box.drawRect(0,0,24,24);
		box.endFill();
		box.lineStyle({ color: isSelected ? 0x605008 : 0x384018, width: 2, alignment: 0 });
		box.moveTo(22,24).lineTo(22,0);
		box.moveTo(24,24).lineTo(0,24);
		c.addChild(box);

		// icon
		const iconSprite = new PIXI.Sprite(PIXI.Texture.from(icon));
		iconSprite.x = 4;
		iconSprite.y = 4;
		iconSprite.tint = {r:32,g:32,b:32};
		c.addChild(iconSprite);

		this.opations.addChild(c);
	}

	updateOpations(selected, onlyShowEquips) {
		for(var i = 0; i < this.opations.children.length; i++) {
			this.opations.removeChild(this.opations.children[i]);
			i--;
		}

		this.addOpation("res/system/weapon.png", 0, selected == 0);
		this.addOpation("res/system/shield.png", 1, selected == 1);
		this.addOpation("res/system/armor.png", 2, selected == 2);
		this.addOpation("res/system/adornment.png", 3, selected == 3);
		if(!onlyShowEquips) {
			this.addOpation("res/system/usable.png", 3, selected == 4);
			this.addOpation("res/system/others.png", 3, selected == 5);
			this.rightButton.x = 248;
		}
		else {
			this.rightButton.x = 201;
		}
	}

	async selectOpation(index, items) {
		if(this._opationIndex==index)
			return;

		this._opationIndex = index;
		this.updateOpations(index, this._onlyShowEquips);

		var items;
		switch(index) {
			case 0: items = await new Promise(resolve => this.getEquipmentList(0, resolve)); break;
			case 1: items = await new Promise(resolve => this.getEquipmentList(1, resolve)); break;
			case 2: items = await new Promise(resolve => this.getEquipmentList(2, resolve)); break;
			case 3: items = await new Promise(resolve => this.getEquipmentList(3, resolve)); break;
			case 4: items = await new Promise(resolve => this.getUsableList(resolve)); break;
			case 5: items = await new Promise(resolve => this.getOtherList(resolve)); break;
		}

		this.menu.clearItems();

		for(var i = 0; i < items.length; i++) {
			const item = items[i];
			const menuItem = new MenuItem(item.name);
			menuItem.enable = this.enableFunc(item);
			menuItem.icon = item.getIcon();
			
			// 添加当前index/total
			const length = `${this.apply(item.url, 0)}/${this.amounts[item.url]||0}`;
			const size = PIXI.TextMetrics.measureText(length, menuItem.enable ? textStyle : unenableStyle).width;
			const literal = new PIXI.Text(length, menuItem.enable ? textStyle : unenableStyle);
			literal.x = 410 - size;
			literal.y = 0;
			literal.resolution = 2;

			if(this._showPrice) {
				const textContainer = new PIXI.Container();
				const price = `${parseInt(item.price * this._priceRadio)}$`;
				const priceLiteral = new PIXI.Text(price, menuItem.enable ? textStyle : unenableStyle);
				priceLiteral.x = 410 - 120;
				priceLiteral.y = 0;
				priceLiteral.resolution = 2;

				textContainer.addChild(literal);
				textContainer.addChild(priceLiteral);
				menuItem.connectContrl = textContainer;
			}
			else {
				menuItem.connectContrl = literal;
			}

			this.menu.addItem(menuItem);
		}

		this.showItems = items;
		if(this.showItems.length > 0) 
			this.menu.selectedIndex = 0;
	}

	setup(enableFunc, zFunc, xFunc, onlyShowEquips, showPrice, priceRadio, items, opationIndex) {
		this._sellItems = items;
		this.enableFunc = enableFunc;
		this.xFunc = xFunc;
		this.zFunc = zFunc;
		this._showPrice = showPrice ?? false;
		this._priceRadio = priceRadio ?? 1;
		this._onlyShowEquips = onlyShowEquips ?? true;
		this._opationIndex = -1;
		this.selectOpation(opationIndex ?? 0);
		this.menu.selectedIndex = 0;

		if(this.descrption)
			this.descrption.reset();

		if(showPrice) {
			if(!this.priceText) {
				// 添加price
				const headLiteral = "PIRCE";
				this.priceText = new PIXI.Text(headLiteral, panelHeadStyle);
				this.priceText.x = this.width - 130;
				this.priceText.y = 2;
				this.priceText.resolution = 2;
				this.addChild(this.priceText);
			}	
		}
		else {
			if(this.priceText) {
				this.removeChild(priceText);
			}
		}
	}

	keyDown(key) {
		switch(key) {
			case "left": this.selectOpation(Math.max(0, this._opationIndex - 1)); break;
			case "right": this.selectOpation(Math.min(5, this._opationIndex + 1)); break;
			case "up": 
				this.menu.up();
				if(this.descrption) 
					this.descrption.show(this.showItems[this.menu.selectedIndex].descrption);
				break;
			case "down": 
				this.menu.down(); 
				if(this.descrption) 
					this.descrption.show(this.showItems[this.menu.selectedIndex].descrption);
				break;
			case "a":
				break;
			case "s":
				if(!this.descrption) {
					// 添加描述控件
					this.descrption = new Descrption(L2D.UIManager);
					this.addChild(this.descrption);
				}

				if(!this.descrption.visual)
					this.descrption.show(this.showItems[this.menu.selectedIndex].descrption);
				else
					this.descrption.close();
				break;
			case "z":
				if(!this.menu.selected.enable) {
					return;
				}

				this.zFunc(this.showItems[this.menu.selectedIndex]); 
				break;
			case "x": this.xFunc(); break;
		}
	}

	/**
	 * 通知背包指定道具占用数量并返回应用后的数量
	 * @param {string} url 道具url 
	 * @param {int} amount 应用数量
	 * @return 返回当前应用数量
	 */
	apply(url, amount) {
		if(!this.items[url])
			this.items[url] = L2D.Loader.load(url);

		const exsitAmount = this.applys[url] ?? 0;
		this.applys[url] = exsitAmount + amount;
		return this.applys[url];
	}

	/**
	 * 向背包中添加指定道具
	 * @param {string} url 道具url 
	 * @param {int} amount 移除数量
	 */
	add(url, amount) {
		if(!this.items[url])
			this.items[url] = L2D.Loader.load(url);
			
		const exsitAmount = this.exist(url);
		this.amounts[url] = exsitAmount + amount;

		// 播放获得道具动画	

	}

	/**
	 * 从背包中移除指定道具
	 * @param {string} url 道具url
	 * @param {int} amount 添加数量 
	 */
	remove(url, amount) {
		const exsitAmount = this.exist(url);
	 	amount = Math.max(0, exsitAmount - amount);

		this.amounts[url] = amount;
		if(amount <= 0) {
			delete this.amounts[url];
		}
	}

	/**
	 * 查询指定道具是否存在于包裹中
	 * @param {string} url 道具url 
	 * @returns 当前道具数量
	 */
	exist(url) {
		if(this.amounts[url])
			return this.amounts[url];

		return 0;
	}

	/**
	 * 获取背包中装备列表
	 * @returns {Item[]} 返回道具实体
	 */
	async getEquipmentList(location, callback) {
		const items = [];
		if(this._sellItems) {
			for(var element in this._sellItems) {
				var item = await global.loadItem(this._sellItems[element].url);
				item = item.clone();
				item.price = this._sellItems[element].price ?? item.price;
				if(item.itemType == 0 && item.location == location) {
					items.push(item);
				}
			}
		}
		else {
			for(var element in this.amounts) {
				if(this.amounts[element] > 0) {
					if(this.items[element].itemType == 0 && this.items[element].location == location) {
						items.push(this.items[element]);
					}
				}
			}
		}
		callback(items);
	}

	/**
	 * 获取背包中消耗品列表
	 * @returns {Item[]} 返回道具实体
	 */
	async getUsableList() {
		const items = [];
		if(this._sellItems) {
			for(var element in this._sellItems) {
				var item = await global.loadItem(this._sellItems[element].url);
				item = item.clone();
				item.price = this._sellItems[element].price ?? item.price;
				if(item.itemType == 1) {
					items.push(item);
				}
			}
		}
		else {
			for(var element in this.amounts) {
				if(this.amounts[element] > 0) {
					if(this.items[element].itemType == 1) {
						items.push(this.items[element]);
					}
				}
			}
		}
		callback(items);
	}

	/**
	 * 获取背包中杂物列表
	 * @returns {Item[]} 返回道具实体
	 */
	async getOtherList() {
		const items = [];
		if(this._sellItems) {
			for(var element in this._sellItems) {
				var item = await global.loadItem(this._sellItems[element].url);
				item = item.clone();
				item.price = this._sellItems[element].price ?? item.price;
				if(item.itemType > 1) {
					items.push(item);
				}
			}
		}
		else {
			for(var element in this.amounts) {
				if(this.amounts[element] > 0) {
					if(this.items[element].itemType > 1) {
						items.push(this.items[element]);
					}
				}
			}
		}
		callback(items);
	}
}

// src/5-9.skill.js

// src/6-1.global.js
class Global {
    constructor() {
        // 存入Character
        this.party = [];
        // 存入Item
        this.bag = new Bag();
        // 设置主要操作Character
        this.player = null;
        // 设置升级表单
        
    }
}

// 初始化公共参数
this.global = new Global();

// 初始化道具数据缓存中心
this.global.itemLoaded = {};
this.global.loadItem = async function(url) {
	async function loadUrl(url, callback) {
		L2D.Loader.load(url, null, function(e) {
			callback(e.content);
		});
	}

	if(!global.itemLoaded[url]) {
		global.itemLoaded[url] = await new Promise(resolve => loadUrl(url, resolve));
	}

	return global.itemLoaded[url];
}
return L2D;
})();
